xref: /freebsd/contrib/wpa/src/eap_common/eap_sim_common.c (revision c1d255d3ffdbe447de3ab875bf4e7d7accc5bfc5)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * EAP peer/server: EAP-SIM/AKA/AKA' shared routines
339beb93cSSam Leffler  * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler 
1139beb93cSSam Leffler #include "common.h"
1239beb93cSSam Leffler #include "wpabuf.h"
13e28a4053SRui Paulo #include "crypto/aes_wrap.h"
14e28a4053SRui Paulo #include "crypto/crypto.h"
15e28a4053SRui Paulo #include "crypto/sha1.h"
16e28a4053SRui Paulo #include "crypto/sha256.h"
17f05cddf9SRui Paulo #include "crypto/random.h"
18e28a4053SRui Paulo #include "eap_common/eap_defs.h"
1939beb93cSSam Leffler #include "eap_common/eap_sim_common.h"
2039beb93cSSam Leffler 
2139beb93cSSam Leffler 
eap_sim_prf(const u8 * key,u8 * x,size_t xlen)2239beb93cSSam Leffler static int eap_sim_prf(const u8 *key, u8 *x, size_t xlen)
2339beb93cSSam Leffler {
2439beb93cSSam Leffler 	return fips186_2_prf(key, EAP_SIM_MK_LEN, x, xlen);
2539beb93cSSam Leffler }
2639beb93cSSam Leffler 
2739beb93cSSam Leffler 
eap_sim_derive_mk(const u8 * identity,size_t identity_len,const u8 * nonce_mt,u16 selected_version,const u8 * ver_list,size_t ver_list_len,int num_chal,const u8 * kc,u8 * mk)2839beb93cSSam Leffler void eap_sim_derive_mk(const u8 *identity, size_t identity_len,
2939beb93cSSam Leffler 		       const u8 *nonce_mt, u16 selected_version,
3039beb93cSSam Leffler 		       const u8 *ver_list, size_t ver_list_len,
3139beb93cSSam Leffler 		       int num_chal, const u8 *kc, u8 *mk)
3239beb93cSSam Leffler {
3339beb93cSSam Leffler 	u8 sel_ver[2];
3439beb93cSSam Leffler 	const unsigned char *addr[5];
3539beb93cSSam Leffler 	size_t len[5];
3639beb93cSSam Leffler 
3739beb93cSSam Leffler 	addr[0] = identity;
3839beb93cSSam Leffler 	len[0] = identity_len;
3939beb93cSSam Leffler 	addr[1] = kc;
4039beb93cSSam Leffler 	len[1] = num_chal * EAP_SIM_KC_LEN;
4139beb93cSSam Leffler 	addr[2] = nonce_mt;
4239beb93cSSam Leffler 	len[2] = EAP_SIM_NONCE_MT_LEN;
4339beb93cSSam Leffler 	addr[3] = ver_list;
4439beb93cSSam Leffler 	len[3] = ver_list_len;
4539beb93cSSam Leffler 	addr[4] = sel_ver;
4639beb93cSSam Leffler 	len[4] = 2;
4739beb93cSSam Leffler 
4839beb93cSSam Leffler 	WPA_PUT_BE16(sel_ver, selected_version);
4939beb93cSSam Leffler 
5039beb93cSSam Leffler 	/* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
5139beb93cSSam Leffler 	sha1_vector(5, addr, len, mk);
5239beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN);
5339beb93cSSam Leffler }
5439beb93cSSam Leffler 
5539beb93cSSam Leffler 
eap_aka_derive_mk(const u8 * identity,size_t identity_len,const u8 * ik,const u8 * ck,u8 * mk)5639beb93cSSam Leffler void eap_aka_derive_mk(const u8 *identity, size_t identity_len,
5739beb93cSSam Leffler 		       const u8 *ik, const u8 *ck, u8 *mk)
5839beb93cSSam Leffler {
5939beb93cSSam Leffler 	const u8 *addr[3];
6039beb93cSSam Leffler 	size_t len[3];
6139beb93cSSam Leffler 
6239beb93cSSam Leffler 	addr[0] = identity;
6339beb93cSSam Leffler 	len[0] = identity_len;
6439beb93cSSam Leffler 	addr[1] = ik;
6539beb93cSSam Leffler 	len[1] = EAP_AKA_IK_LEN;
6639beb93cSSam Leffler 	addr[2] = ck;
6739beb93cSSam Leffler 	len[2] = EAP_AKA_CK_LEN;
6839beb93cSSam Leffler 
6939beb93cSSam Leffler 	/* MK = SHA1(Identity|IK|CK) */
7039beb93cSSam Leffler 	sha1_vector(3, addr, len, mk);
7139beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", ik, EAP_AKA_IK_LEN);
7239beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", ck, EAP_AKA_CK_LEN);
7339beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: MK", mk, EAP_SIM_MK_LEN);
7439beb93cSSam Leffler }
7539beb93cSSam Leffler 
7639beb93cSSam Leffler 
eap_sim_derive_keys(const u8 * mk,u8 * k_encr,u8 * k_aut,u8 * msk,u8 * emsk)7739beb93cSSam Leffler int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk, u8 *emsk)
7839beb93cSSam Leffler {
7939beb93cSSam Leffler 	u8 buf[EAP_SIM_K_ENCR_LEN + EAP_SIM_K_AUT_LEN +
8039beb93cSSam Leffler 	       EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN], *pos;
8139beb93cSSam Leffler 	if (eap_sim_prf(mk, buf, sizeof(buf)) < 0) {
8239beb93cSSam Leffler 		wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys");
8339beb93cSSam Leffler 		return -1;
8439beb93cSSam Leffler 	}
8539beb93cSSam Leffler 	pos = buf;
8639beb93cSSam Leffler 	os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN);
8739beb93cSSam Leffler 	pos += EAP_SIM_K_ENCR_LEN;
8839beb93cSSam Leffler 	os_memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN);
8939beb93cSSam Leffler 	pos += EAP_SIM_K_AUT_LEN;
9039beb93cSSam Leffler 	os_memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN);
9139beb93cSSam Leffler 	pos += EAP_SIM_KEYING_DATA_LEN;
9239beb93cSSam Leffler 	os_memcpy(emsk, pos, EAP_EMSK_LEN);
9339beb93cSSam Leffler 
9439beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr",
9539beb93cSSam Leffler 			k_encr, EAP_SIM_K_ENCR_LEN);
9639beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut",
9739beb93cSSam Leffler 			k_aut, EAP_SIM_K_AUT_LEN);
9839beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material (MSK)",
9939beb93cSSam Leffler 			msk, EAP_SIM_KEYING_DATA_LEN);
10039beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN);
10139beb93cSSam Leffler 	os_memset(buf, 0, sizeof(buf));
10239beb93cSSam Leffler 
10339beb93cSSam Leffler 	return 0;
10439beb93cSSam Leffler }
10539beb93cSSam Leffler 
10639beb93cSSam Leffler 
eap_sim_derive_keys_reauth(u16 _counter,const u8 * identity,size_t identity_len,const u8 * nonce_s,const u8 * mk,u8 * msk,u8 * emsk)10739beb93cSSam Leffler int eap_sim_derive_keys_reauth(u16 _counter,
10839beb93cSSam Leffler 			       const u8 *identity, size_t identity_len,
10939beb93cSSam Leffler 			       const u8 *nonce_s, const u8 *mk, u8 *msk,
11039beb93cSSam Leffler 			       u8 *emsk)
11139beb93cSSam Leffler {
11239beb93cSSam Leffler 	u8 xkey[SHA1_MAC_LEN];
11339beb93cSSam Leffler 	u8 buf[EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN + 32];
11439beb93cSSam Leffler 	u8 counter[2];
11539beb93cSSam Leffler 	const u8 *addr[4];
11639beb93cSSam Leffler 	size_t len[4];
11739beb93cSSam Leffler 
11839beb93cSSam Leffler 	while (identity_len > 0 && identity[identity_len - 1] == 0) {
11939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop null "
12039beb93cSSam Leffler 			   "character from the end of identity");
12139beb93cSSam Leffler 		identity_len--;
12239beb93cSSam Leffler 	}
12339beb93cSSam Leffler 	addr[0] = identity;
12439beb93cSSam Leffler 	len[0] = identity_len;
12539beb93cSSam Leffler 	addr[1] = counter;
12639beb93cSSam Leffler 	len[1] = 2;
12739beb93cSSam Leffler 	addr[2] = nonce_s;
12839beb93cSSam Leffler 	len[2] = EAP_SIM_NONCE_S_LEN;
12939beb93cSSam Leffler 	addr[3] = mk;
13039beb93cSSam Leffler 	len[3] = EAP_SIM_MK_LEN;
13139beb93cSSam Leffler 
13239beb93cSSam Leffler 	WPA_PUT_BE16(counter, _counter);
13339beb93cSSam Leffler 
13439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth");
13539beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
13639beb93cSSam Leffler 			  identity, identity_len);
13739beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "EAP-SIM: counter", counter, 2);
13839beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "EAP-SIM: NONCE_S", nonce_s,
13939beb93cSSam Leffler 		    EAP_SIM_NONCE_S_LEN);
14039beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN);
14139beb93cSSam Leffler 
14239beb93cSSam Leffler 	/* XKEY' = SHA1(Identity|counter|NONCE_S|MK) */
14339beb93cSSam Leffler 	sha1_vector(4, addr, len, xkey);
14439beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN);
14539beb93cSSam Leffler 
14639beb93cSSam Leffler 	if (eap_sim_prf(xkey, buf, sizeof(buf)) < 0) {
14739beb93cSSam Leffler 		wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys");
14839beb93cSSam Leffler 		return -1;
14939beb93cSSam Leffler 	}
15039beb93cSSam Leffler 	if (msk) {
15139beb93cSSam Leffler 		os_memcpy(msk, buf, EAP_SIM_KEYING_DATA_LEN);
15239beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material (MSK)",
15339beb93cSSam Leffler 			    msk, EAP_SIM_KEYING_DATA_LEN);
15439beb93cSSam Leffler 	}
15539beb93cSSam Leffler 	if (emsk) {
15639beb93cSSam Leffler 		os_memcpy(emsk, buf + EAP_SIM_KEYING_DATA_LEN, EAP_EMSK_LEN);
15739beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN);
15839beb93cSSam Leffler 	}
15939beb93cSSam Leffler 	os_memset(buf, 0, sizeof(buf));
16039beb93cSSam Leffler 
16139beb93cSSam Leffler 	return 0;
16239beb93cSSam Leffler }
16339beb93cSSam Leffler 
16439beb93cSSam Leffler 
eap_sim_verify_mac(const u8 * k_aut,const struct wpabuf * req,const u8 * mac,const u8 * extra,size_t extra_len)16539beb93cSSam Leffler int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req,
16639beb93cSSam Leffler 		       const u8 *mac, const u8 *extra, size_t extra_len)
16739beb93cSSam Leffler {
16839beb93cSSam Leffler 	unsigned char hmac[SHA1_MAC_LEN];
16939beb93cSSam Leffler 	const u8 *addr[2];
17039beb93cSSam Leffler 	size_t len[2];
17139beb93cSSam Leffler 	u8 *tmp;
17239beb93cSSam Leffler 
17339beb93cSSam Leffler 	if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN ||
17439beb93cSSam Leffler 	    mac < wpabuf_head_u8(req) ||
17539beb93cSSam Leffler 	    mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
17639beb93cSSam Leffler 		return -1;
17739beb93cSSam Leffler 
17885732ac8SCy Schubert 	tmp = os_memdup(wpabuf_head(req), wpabuf_len(req));
17939beb93cSSam Leffler 	if (tmp == NULL)
18039beb93cSSam Leffler 		return -1;
18139beb93cSSam Leffler 
18239beb93cSSam Leffler 	addr[0] = tmp;
18339beb93cSSam Leffler 	len[0] = wpabuf_len(req);
18439beb93cSSam Leffler 	addr[1] = extra;
18539beb93cSSam Leffler 	len[1] = extra_len;
18639beb93cSSam Leffler 
18739beb93cSSam Leffler 	/* HMAC-SHA1-128 */
18839beb93cSSam Leffler 	os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
18939beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg",
19039beb93cSSam Leffler 		    tmp, wpabuf_len(req));
19139beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - extra data",
19239beb93cSSam Leffler 		    extra, extra_len);
19339beb93cSSam Leffler 	wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Verify MAC - K_aut",
19439beb93cSSam Leffler 			k_aut, EAP_SIM_K_AUT_LEN);
19539beb93cSSam Leffler 	hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
19639beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC: MAC",
19739beb93cSSam Leffler 		    hmac, EAP_SIM_MAC_LEN);
19839beb93cSSam Leffler 	os_free(tmp);
19939beb93cSSam Leffler 
2005b9c547cSRui Paulo 	return (os_memcmp_const(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
20139beb93cSSam Leffler }
20239beb93cSSam Leffler 
20339beb93cSSam Leffler 
eap_sim_add_mac(const u8 * k_aut,const u8 * msg,size_t msg_len,u8 * mac,const u8 * extra,size_t extra_len)20439beb93cSSam Leffler void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac,
20539beb93cSSam Leffler 		     const u8 *extra, size_t extra_len)
20639beb93cSSam Leffler {
20739beb93cSSam Leffler 	unsigned char hmac[SHA1_MAC_LEN];
20839beb93cSSam Leffler 	const u8 *addr[2];
20939beb93cSSam Leffler 	size_t len[2];
21039beb93cSSam Leffler 
21139beb93cSSam Leffler 	addr[0] = msg;
21239beb93cSSam Leffler 	len[0] = msg_len;
21339beb93cSSam Leffler 	addr[1] = extra;
21439beb93cSSam Leffler 	len[1] = extra_len;
21539beb93cSSam Leffler 
21639beb93cSSam Leffler 	/* HMAC-SHA1-128 */
21739beb93cSSam Leffler 	os_memset(mac, 0, EAP_SIM_MAC_LEN);
21839beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - msg", msg, msg_len);
21939beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - extra data",
22039beb93cSSam Leffler 		    extra, extra_len);
22139beb93cSSam Leffler 	wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Add MAC - K_aut",
22239beb93cSSam Leffler 			k_aut, EAP_SIM_K_AUT_LEN);
22339beb93cSSam Leffler 	hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
22439beb93cSSam Leffler 	os_memcpy(mac, hmac, EAP_SIM_MAC_LEN);
22539beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC: MAC",
22639beb93cSSam Leffler 		    mac, EAP_SIM_MAC_LEN);
22739beb93cSSam Leffler }
22839beb93cSSam Leffler 
22939beb93cSSam Leffler 
230e28a4053SRui Paulo #if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
prf_prime(const u8 * k,const char * seed1,const u8 * seed2,size_t seed2_len,const u8 * seed3,size_t seed3_len,u8 * res,size_t res_len)23139beb93cSSam Leffler static void prf_prime(const u8 *k, const char *seed1,
23239beb93cSSam Leffler 		      const u8 *seed2, size_t seed2_len,
23339beb93cSSam Leffler 		      const u8 *seed3, size_t seed3_len,
23439beb93cSSam Leffler 		      u8 *res, size_t res_len)
23539beb93cSSam Leffler {
23639beb93cSSam Leffler 	const u8 *addr[5];
23739beb93cSSam Leffler 	size_t len[5];
23839beb93cSSam Leffler 	u8 hash[SHA256_MAC_LEN];
23939beb93cSSam Leffler 	u8 iter;
24039beb93cSSam Leffler 
24139beb93cSSam Leffler 	/*
24239beb93cSSam Leffler 	 * PRF'(K,S) = T1 | T2 | T3 | T4 | ...
24339beb93cSSam Leffler 	 * T1 = HMAC-SHA-256 (K, S | 0x01)
24439beb93cSSam Leffler 	 * T2 = HMAC-SHA-256 (K, T1 | S | 0x02)
24539beb93cSSam Leffler 	 * T3 = HMAC-SHA-256 (K, T2 | S | 0x03)
24639beb93cSSam Leffler 	 * T4 = HMAC-SHA-256 (K, T3 | S | 0x04)
24739beb93cSSam Leffler 	 * ...
24839beb93cSSam Leffler 	 */
24939beb93cSSam Leffler 
25039beb93cSSam Leffler 	addr[0] = hash;
25139beb93cSSam Leffler 	len[0] = 0;
25239beb93cSSam Leffler 	addr[1] = (const u8 *) seed1;
25339beb93cSSam Leffler 	len[1] = os_strlen(seed1);
25439beb93cSSam Leffler 	addr[2] = seed2;
25539beb93cSSam Leffler 	len[2] = seed2_len;
25639beb93cSSam Leffler 	addr[3] = seed3;
25739beb93cSSam Leffler 	len[3] = seed3_len;
25839beb93cSSam Leffler 	addr[4] = &iter;
25939beb93cSSam Leffler 	len[4] = 1;
26039beb93cSSam Leffler 
26139beb93cSSam Leffler 	iter = 0;
26239beb93cSSam Leffler 	while (res_len) {
26339beb93cSSam Leffler 		size_t hlen;
26439beb93cSSam Leffler 		iter++;
26539beb93cSSam Leffler 		hmac_sha256_vector(k, 32, 5, addr, len, hash);
26639beb93cSSam Leffler 		len[0] = SHA256_MAC_LEN;
26739beb93cSSam Leffler 		hlen = res_len > SHA256_MAC_LEN ? SHA256_MAC_LEN : res_len;
26839beb93cSSam Leffler 		os_memcpy(res, hash, hlen);
26939beb93cSSam Leffler 		res += hlen;
27039beb93cSSam Leffler 		res_len -= hlen;
27139beb93cSSam Leffler 	}
27239beb93cSSam Leffler }
27339beb93cSSam Leffler 
27439beb93cSSam Leffler 
eap_aka_prime_derive_keys(const u8 * identity,size_t identity_len,const u8 * ik,const u8 * ck,u8 * k_encr,u8 * k_aut,u8 * k_re,u8 * msk,u8 * emsk)27539beb93cSSam Leffler void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len,
27639beb93cSSam Leffler 			       const u8 *ik, const u8 *ck, u8 *k_encr,
27739beb93cSSam Leffler 			       u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk)
27839beb93cSSam Leffler {
27939beb93cSSam Leffler 	u8 key[EAP_AKA_IK_LEN + EAP_AKA_CK_LEN];
28039beb93cSSam Leffler 	u8 keys[EAP_SIM_K_ENCR_LEN + EAP_AKA_PRIME_K_AUT_LEN +
28139beb93cSSam Leffler 		EAP_AKA_PRIME_K_RE_LEN + EAP_MSK_LEN + EAP_EMSK_LEN];
28239beb93cSSam Leffler 	u8 *pos;
28339beb93cSSam Leffler 
28439beb93cSSam Leffler 	/*
28539beb93cSSam Leffler 	 * MK = PRF'(IK'|CK',"EAP-AKA'"|Identity)
28639beb93cSSam Leffler 	 * K_encr = MK[0..127]
28739beb93cSSam Leffler 	 * K_aut  = MK[128..383]
28839beb93cSSam Leffler 	 * K_re   = MK[384..639]
28939beb93cSSam Leffler 	 * MSK    = MK[640..1151]
29039beb93cSSam Leffler 	 * EMSK   = MK[1152..1663]
29139beb93cSSam Leffler 	 */
29239beb93cSSam Leffler 
29339beb93cSSam Leffler 	os_memcpy(key, ik, EAP_AKA_IK_LEN);
29439beb93cSSam Leffler 	os_memcpy(key + EAP_AKA_IK_LEN, ck, EAP_AKA_CK_LEN);
29539beb93cSSam Leffler 
29639beb93cSSam Leffler 	prf_prime(key, "EAP-AKA'", identity, identity_len, NULL, 0,
29739beb93cSSam Leffler 		  keys, sizeof(keys));
29839beb93cSSam Leffler 
29939beb93cSSam Leffler 	pos = keys;
30039beb93cSSam Leffler 	os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN);
30139beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_encr",
30239beb93cSSam Leffler 			k_encr, EAP_SIM_K_ENCR_LEN);
30339beb93cSSam Leffler 	pos += EAP_SIM_K_ENCR_LEN;
30439beb93cSSam Leffler 
30539beb93cSSam Leffler 	os_memcpy(k_aut, pos, EAP_AKA_PRIME_K_AUT_LEN);
30639beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_aut",
30739beb93cSSam Leffler 			k_aut, EAP_AKA_PRIME_K_AUT_LEN);
30839beb93cSSam Leffler 	pos += EAP_AKA_PRIME_K_AUT_LEN;
30939beb93cSSam Leffler 
31039beb93cSSam Leffler 	os_memcpy(k_re, pos, EAP_AKA_PRIME_K_RE_LEN);
31139beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_re",
31239beb93cSSam Leffler 			k_re, EAP_AKA_PRIME_K_RE_LEN);
31339beb93cSSam Leffler 	pos += EAP_AKA_PRIME_K_RE_LEN;
31439beb93cSSam Leffler 
31539beb93cSSam Leffler 	os_memcpy(msk, pos, EAP_MSK_LEN);
31639beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN);
31739beb93cSSam Leffler 	pos += EAP_MSK_LEN;
31839beb93cSSam Leffler 
31939beb93cSSam Leffler 	os_memcpy(emsk, pos, EAP_EMSK_LEN);
32039beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN);
32139beb93cSSam Leffler }
32239beb93cSSam Leffler 
32339beb93cSSam Leffler 
eap_aka_prime_derive_keys_reauth(const u8 * k_re,u16 counter,const u8 * identity,size_t identity_len,const u8 * nonce_s,u8 * msk,u8 * emsk)32439beb93cSSam Leffler int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
32539beb93cSSam Leffler 				     const u8 *identity, size_t identity_len,
32639beb93cSSam Leffler 				     const u8 *nonce_s, u8 *msk, u8 *emsk)
32739beb93cSSam Leffler {
32839beb93cSSam Leffler 	u8 seed3[2 + EAP_SIM_NONCE_S_LEN];
32939beb93cSSam Leffler 	u8 keys[EAP_MSK_LEN + EAP_EMSK_LEN];
33039beb93cSSam Leffler 	u8 *pos;
33139beb93cSSam Leffler 
33239beb93cSSam Leffler 	/*
33339beb93cSSam Leffler 	 * MK = PRF'(K_re,"EAP-AKA' re-auth"|Identity|counter|NONCE_S)
33439beb93cSSam Leffler 	 * MSK  = MK[0..511]
33539beb93cSSam Leffler 	 * EMSK = MK[512..1023]
33639beb93cSSam Leffler 	 */
33739beb93cSSam Leffler 
33839beb93cSSam Leffler 	WPA_PUT_BE16(seed3, counter);
33939beb93cSSam Leffler 	os_memcpy(seed3 + 2, nonce_s, EAP_SIM_NONCE_S_LEN);
34039beb93cSSam Leffler 
34139beb93cSSam Leffler 	prf_prime(k_re, "EAP-AKA' re-auth", identity, identity_len,
34239beb93cSSam Leffler 		  seed3, sizeof(seed3),
34339beb93cSSam Leffler 		  keys, sizeof(keys));
34439beb93cSSam Leffler 
34539beb93cSSam Leffler 	pos = keys;
34639beb93cSSam Leffler 	os_memcpy(msk, pos, EAP_MSK_LEN);
34739beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN);
34839beb93cSSam Leffler 	pos += EAP_MSK_LEN;
34939beb93cSSam Leffler 
35039beb93cSSam Leffler 	os_memcpy(emsk, pos, EAP_EMSK_LEN);
35139beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN);
35239beb93cSSam Leffler 
35339beb93cSSam Leffler 	os_memset(keys, 0, sizeof(keys));
35439beb93cSSam Leffler 
35539beb93cSSam Leffler 	return 0;
35639beb93cSSam Leffler }
35739beb93cSSam Leffler 
35839beb93cSSam Leffler 
eap_sim_verify_mac_sha256(const u8 * k_aut,const struct wpabuf * req,const u8 * mac,const u8 * extra,size_t extra_len)35939beb93cSSam Leffler int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req,
36039beb93cSSam Leffler 			      const u8 *mac, const u8 *extra, size_t extra_len)
36139beb93cSSam Leffler {
36239beb93cSSam Leffler 	unsigned char hmac[SHA256_MAC_LEN];
36339beb93cSSam Leffler 	const u8 *addr[2];
36439beb93cSSam Leffler 	size_t len[2];
36539beb93cSSam Leffler 	u8 *tmp;
36639beb93cSSam Leffler 
36739beb93cSSam Leffler 	if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN ||
36839beb93cSSam Leffler 	    mac < wpabuf_head_u8(req) ||
36939beb93cSSam Leffler 	    mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
37039beb93cSSam Leffler 		return -1;
37139beb93cSSam Leffler 
37285732ac8SCy Schubert 	tmp = os_memdup(wpabuf_head(req), wpabuf_len(req));
37339beb93cSSam Leffler 	if (tmp == NULL)
37439beb93cSSam Leffler 		return -1;
37539beb93cSSam Leffler 
37639beb93cSSam Leffler 	addr[0] = tmp;
37739beb93cSSam Leffler 	len[0] = wpabuf_len(req);
37839beb93cSSam Leffler 	addr[1] = extra;
37939beb93cSSam Leffler 	len[1] = extra_len;
38039beb93cSSam Leffler 
38139beb93cSSam Leffler 	/* HMAC-SHA-256-128 */
38239beb93cSSam Leffler 	os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
38339beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - msg",
38439beb93cSSam Leffler 		    tmp, wpabuf_len(req));
38539beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - extra data",
38639beb93cSSam Leffler 		    extra, extra_len);
38739beb93cSSam Leffler 	wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Verify MAC - K_aut",
38839beb93cSSam Leffler 			k_aut, EAP_AKA_PRIME_K_AUT_LEN);
38939beb93cSSam Leffler 	hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac);
39039beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC: MAC",
39139beb93cSSam Leffler 		    hmac, EAP_SIM_MAC_LEN);
39239beb93cSSam Leffler 	os_free(tmp);
39339beb93cSSam Leffler 
3945b9c547cSRui Paulo 	return (os_memcmp_const(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
39539beb93cSSam Leffler }
39639beb93cSSam Leffler 
39739beb93cSSam Leffler 
eap_sim_add_mac_sha256(const u8 * k_aut,const u8 * msg,size_t msg_len,u8 * mac,const u8 * extra,size_t extra_len)39839beb93cSSam Leffler void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len,
39939beb93cSSam Leffler 			    u8 *mac, const u8 *extra, size_t extra_len)
40039beb93cSSam Leffler {
40139beb93cSSam Leffler 	unsigned char hmac[SHA256_MAC_LEN];
40239beb93cSSam Leffler 	const u8 *addr[2];
40339beb93cSSam Leffler 	size_t len[2];
40439beb93cSSam Leffler 
40539beb93cSSam Leffler 	addr[0] = msg;
40639beb93cSSam Leffler 	len[0] = msg_len;
40739beb93cSSam Leffler 	addr[1] = extra;
40839beb93cSSam Leffler 	len[1] = extra_len;
40939beb93cSSam Leffler 
41039beb93cSSam Leffler 	/* HMAC-SHA-256-128 */
41139beb93cSSam Leffler 	os_memset(mac, 0, EAP_SIM_MAC_LEN);
41239beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - msg", msg, msg_len);
41339beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - extra data",
41439beb93cSSam Leffler 		    extra, extra_len);
41539beb93cSSam Leffler 	wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Add MAC - K_aut",
41639beb93cSSam Leffler 			k_aut, EAP_AKA_PRIME_K_AUT_LEN);
41739beb93cSSam Leffler 	hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac);
41839beb93cSSam Leffler 	os_memcpy(mac, hmac, EAP_SIM_MAC_LEN);
41939beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC: MAC",
42039beb93cSSam Leffler 		    mac, EAP_SIM_MAC_LEN);
42139beb93cSSam Leffler }
42239beb93cSSam Leffler 
42339beb93cSSam Leffler 
eap_aka_prime_derive_ck_ik_prime(u8 * ck,u8 * ik,const u8 * sqn_ak,const u8 * network_name,size_t network_name_len)42439beb93cSSam Leffler void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak,
42539beb93cSSam Leffler 				      const u8 *network_name,
42639beb93cSSam Leffler 				      size_t network_name_len)
42739beb93cSSam Leffler {
42839beb93cSSam Leffler 	u8 key[EAP_AKA_CK_LEN + EAP_AKA_IK_LEN];
42939beb93cSSam Leffler 	u8 hash[SHA256_MAC_LEN];
43039beb93cSSam Leffler 	const u8 *addr[5];
43139beb93cSSam Leffler 	size_t len[5];
43239beb93cSSam Leffler 	u8 fc;
43339beb93cSSam Leffler 	u8 l0[2], l1[2];
43439beb93cSSam Leffler 
43539beb93cSSam Leffler 	/* 3GPP TS 33.402 V8.0.0
43639beb93cSSam Leffler 	 * (CK', IK') = F(CK, IK, <access network identity>)
43739beb93cSSam Leffler 	 */
43839beb93cSSam Leffler 	/* TODO: CK', IK' generation should really be moved into the actual
43939beb93cSSam Leffler 	 * AKA procedure with network name passed in there and option to use
44039beb93cSSam Leffler 	 * AMF separation bit = 1 (3GPP TS 33.401). */
44139beb93cSSam Leffler 
44239beb93cSSam Leffler 	/* Change Request 33.402 CR 0033 to version 8.1.1 from
44339beb93cSSam Leffler 	 * 3GPP TSG-SA WG3 Meeting #53 in September 2008:
44439beb93cSSam Leffler 	 *
44539beb93cSSam Leffler 	 * CK' || IK' = HMAC-SHA-256(Key, S)
44639beb93cSSam Leffler 	 * S = FC || P0 || L0 || P1 || L1 || ... || Pn || Ln
44739beb93cSSam Leffler 	 * Key = CK || IK
44839beb93cSSam Leffler 	 * FC = 0x20
44939beb93cSSam Leffler 	 * P0 = access network identity (3GPP TS 24.302)
45039beb93cSSam Leffler 	 * L0 = length of acceess network identity (2 octets, big endian)
45139beb93cSSam Leffler 	 * P1 = SQN xor AK (if AK is not used, AK is treaded as 000..0
45239beb93cSSam Leffler 	 * L1 = 0x00 0x06
45339beb93cSSam Leffler 	 */
45439beb93cSSam Leffler 
45539beb93cSSam Leffler 	fc = 0x20;
45639beb93cSSam Leffler 
45739beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-AKA': Derive (CK',IK') from (CK,IK)");
45839beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK", ck, EAP_AKA_CK_LEN);
45939beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK", ik, EAP_AKA_IK_LEN);
46039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-AKA': FC = 0x%x", fc);
46139beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': P0 = Access network identity",
46239beb93cSSam Leffler 			  network_name, network_name_len);
46339beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "EAP-AKA': P1 = SQN xor AK", sqn_ak, 6);
46439beb93cSSam Leffler 
46539beb93cSSam Leffler 	os_memcpy(key, ck, EAP_AKA_CK_LEN);
46639beb93cSSam Leffler 	os_memcpy(key + EAP_AKA_CK_LEN, ik, EAP_AKA_IK_LEN);
46739beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': Key = CK || IK",
46839beb93cSSam Leffler 			key, sizeof(key));
46939beb93cSSam Leffler 
47039beb93cSSam Leffler 	addr[0] = &fc;
47139beb93cSSam Leffler 	len[0] = 1;
47239beb93cSSam Leffler 	addr[1] = network_name;
47339beb93cSSam Leffler 	len[1] = network_name_len;
47439beb93cSSam Leffler 	WPA_PUT_BE16(l0, network_name_len);
47539beb93cSSam Leffler 	addr[2] = l0;
47639beb93cSSam Leffler 	len[2] = 2;
47739beb93cSSam Leffler 	addr[3] = sqn_ak;
47839beb93cSSam Leffler 	len[3] = 6;
47939beb93cSSam Leffler 	WPA_PUT_BE16(l1, 6);
48039beb93cSSam Leffler 	addr[4] = l1;
48139beb93cSSam Leffler 	len[4] = 2;
48239beb93cSSam Leffler 
48339beb93cSSam Leffler 	hmac_sha256_vector(key, sizeof(key), 5, addr, len, hash);
48439beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': KDF output (CK' || IK')",
48539beb93cSSam Leffler 			hash, sizeof(hash));
48639beb93cSSam Leffler 
48739beb93cSSam Leffler 	os_memcpy(ck, hash, EAP_AKA_CK_LEN);
48839beb93cSSam Leffler 	os_memcpy(ik, hash + EAP_AKA_CK_LEN, EAP_AKA_IK_LEN);
48939beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK'", ck, EAP_AKA_CK_LEN);
49039beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK'", ik, EAP_AKA_IK_LEN);
49139beb93cSSam Leffler }
492e28a4053SRui Paulo #endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
49339beb93cSSam Leffler 
49439beb93cSSam Leffler 
eap_sim_parse_attr(const u8 * start,const u8 * end,struct eap_sim_attrs * attr,int aka,int encr)49539beb93cSSam Leffler int eap_sim_parse_attr(const u8 *start, const u8 *end,
49639beb93cSSam Leffler 		       struct eap_sim_attrs *attr, int aka, int encr)
49739beb93cSSam Leffler {
49839beb93cSSam Leffler 	const u8 *pos = start, *apos;
49939beb93cSSam Leffler 	size_t alen, plen, i, list_len;
50039beb93cSSam Leffler 
50139beb93cSSam Leffler 	os_memset(attr, 0, sizeof(*attr));
50239beb93cSSam Leffler 	attr->id_req = NO_ID_REQ;
50339beb93cSSam Leffler 	attr->notification = -1;
50439beb93cSSam Leffler 	attr->counter = -1;
50539beb93cSSam Leffler 	attr->selected_version = -1;
50639beb93cSSam Leffler 	attr->client_error_code = -1;
50739beb93cSSam Leffler 
50839beb93cSSam Leffler 	while (pos < end) {
50939beb93cSSam Leffler 		if (pos + 2 > end) {
51039beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow(1)");
51139beb93cSSam Leffler 			return -1;
51239beb93cSSam Leffler 		}
51339beb93cSSam Leffler 		wpa_printf(MSG_MSGDUMP, "EAP-SIM: Attribute: Type=%d Len=%d",
51439beb93cSSam Leffler 			   pos[0], pos[1] * 4);
51539beb93cSSam Leffler 		if (pos + pos[1] * 4 > end) {
51639beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow "
51739beb93cSSam Leffler 				   "(pos=%p len=%d end=%p)",
51839beb93cSSam Leffler 				   pos, pos[1] * 4, end);
51939beb93cSSam Leffler 			return -1;
52039beb93cSSam Leffler 		}
52139beb93cSSam Leffler 		if (pos[1] == 0) {
52239beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-SIM: Attribute underflow");
52339beb93cSSam Leffler 			return -1;
52439beb93cSSam Leffler 		}
52539beb93cSSam Leffler 		apos = pos + 2;
52639beb93cSSam Leffler 		alen = pos[1] * 4 - 2;
52739beb93cSSam Leffler 		wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Attribute data",
52839beb93cSSam Leffler 			    apos, alen);
52939beb93cSSam Leffler 
53039beb93cSSam Leffler 		switch (pos[0]) {
53139beb93cSSam Leffler 		case EAP_SIM_AT_RAND:
53239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RAND");
53339beb93cSSam Leffler 			apos += 2;
53439beb93cSSam Leffler 			alen -= 2;
53539beb93cSSam Leffler 			if ((!aka && (alen % GSM_RAND_LEN)) ||
53639beb93cSSam Leffler 			    (aka && alen != EAP_AKA_RAND_LEN)) {
53739beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND"
53839beb93cSSam Leffler 					   " (len %lu)",
53939beb93cSSam Leffler 					   (unsigned long) alen);
54039beb93cSSam Leffler 				return -1;
54139beb93cSSam Leffler 			}
54239beb93cSSam Leffler 			attr->rand = apos;
54339beb93cSSam Leffler 			attr->num_chal = alen / GSM_RAND_LEN;
54439beb93cSSam Leffler 			break;
54539beb93cSSam Leffler 		case EAP_SIM_AT_AUTN:
54639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTN");
54739beb93cSSam Leffler 			if (!aka) {
54839beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "EAP-SIM: "
54939beb93cSSam Leffler 					   "Unexpected AT_AUTN");
55039beb93cSSam Leffler 				return -1;
55139beb93cSSam Leffler 			}
55239beb93cSSam Leffler 			apos += 2;
55339beb93cSSam Leffler 			alen -= 2;
55439beb93cSSam Leffler 			if (alen != EAP_AKA_AUTN_LEN) {
55539beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN"
55639beb93cSSam Leffler 					   " (len %lu)",
55739beb93cSSam Leffler 					   (unsigned long) alen);
55839beb93cSSam Leffler 				return -1;
55939beb93cSSam Leffler 			}
56039beb93cSSam Leffler 			attr->autn = apos;
56139beb93cSSam Leffler 			break;
56239beb93cSSam Leffler 		case EAP_SIM_AT_PADDING:
56339beb93cSSam Leffler 			if (!encr) {
56439beb93cSSam Leffler 				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
56539beb93cSSam Leffler 					   "AT_PADDING");
56639beb93cSSam Leffler 				return -1;
56739beb93cSSam Leffler 			}
56839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_PADDING");
56939beb93cSSam Leffler 			for (i = 2; i < alen; i++) {
57039beb93cSSam Leffler 				if (apos[i] != 0) {
57139beb93cSSam Leffler 					wpa_printf(MSG_INFO, "EAP-SIM: (encr) "
57239beb93cSSam Leffler 						   "AT_PADDING used a non-zero"
57339beb93cSSam Leffler 						   " padding byte");
57439beb93cSSam Leffler 					wpa_hexdump(MSG_DEBUG, "EAP-SIM: "
57539beb93cSSam Leffler 						    "(encr) padding bytes",
57639beb93cSSam Leffler 						    apos + 2, alen - 2);
57739beb93cSSam Leffler 					return -1;
57839beb93cSSam Leffler 				}
57939beb93cSSam Leffler 			}
58039beb93cSSam Leffler 			break;
58139beb93cSSam Leffler 		case EAP_SIM_AT_NONCE_MT:
58239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NONCE_MT");
58339beb93cSSam Leffler 			if (alen != 2 + EAP_SIM_NONCE_MT_LEN) {
58439beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
58539beb93cSSam Leffler 					   "AT_NONCE_MT length");
58639beb93cSSam Leffler 				return -1;
58739beb93cSSam Leffler 			}
58839beb93cSSam Leffler 			attr->nonce_mt = apos + 2;
58939beb93cSSam Leffler 			break;
59039beb93cSSam Leffler 		case EAP_SIM_AT_PERMANENT_ID_REQ:
59139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_PERMANENT_ID_REQ");
59239beb93cSSam Leffler 			attr->id_req = PERMANENT_ID;
59339beb93cSSam Leffler 			break;
59439beb93cSSam Leffler 		case EAP_SIM_AT_MAC:
59539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_MAC");
59639beb93cSSam Leffler 			if (alen != 2 + EAP_SIM_MAC_LEN) {
59739beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_MAC "
59839beb93cSSam Leffler 					   "length");
59939beb93cSSam Leffler 				return -1;
60039beb93cSSam Leffler 			}
60139beb93cSSam Leffler 			attr->mac = apos + 2;
60239beb93cSSam Leffler 			break;
60339beb93cSSam Leffler 		case EAP_SIM_AT_NOTIFICATION:
60439beb93cSSam Leffler 			if (alen != 2) {
60539beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
60639beb93cSSam Leffler 					   "AT_NOTIFICATION length %lu",
60739beb93cSSam Leffler 					   (unsigned long) alen);
60839beb93cSSam Leffler 				return -1;
60939beb93cSSam Leffler 			}
61039beb93cSSam Leffler 			attr->notification = apos[0] * 256 + apos[1];
61139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NOTIFICATION %d",
61239beb93cSSam Leffler 				   attr->notification);
61339beb93cSSam Leffler 			break;
61439beb93cSSam Leffler 		case EAP_SIM_AT_ANY_ID_REQ:
61539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ANY_ID_REQ");
61639beb93cSSam Leffler 			attr->id_req = ANY_ID;
61739beb93cSSam Leffler 			break;
61839beb93cSSam Leffler 		case EAP_SIM_AT_IDENTITY:
61939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY");
62039beb93cSSam Leffler 			plen = WPA_GET_BE16(apos);
62139beb93cSSam Leffler 			apos += 2;
62239beb93cSSam Leffler 			alen -= 2;
62339beb93cSSam Leffler 			if (plen > alen) {
62439beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
62539beb93cSSam Leffler 					   "AT_IDENTITY (Actual Length %lu, "
62639beb93cSSam Leffler 					   "remaining length %lu)",
62739beb93cSSam Leffler 					   (unsigned long) plen,
62839beb93cSSam Leffler 					   (unsigned long) alen);
62939beb93cSSam Leffler 				return -1;
63039beb93cSSam Leffler 			}
63139beb93cSSam Leffler 
63239beb93cSSam Leffler 			attr->identity = apos;
63339beb93cSSam Leffler 			attr->identity_len = plen;
63439beb93cSSam Leffler 			break;
63539beb93cSSam Leffler 		case EAP_SIM_AT_VERSION_LIST:
63639beb93cSSam Leffler 			if (aka) {
63739beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "EAP-AKA: "
63839beb93cSSam Leffler 					   "Unexpected AT_VERSION_LIST");
63939beb93cSSam Leffler 				return -1;
64039beb93cSSam Leffler 			}
64139beb93cSSam Leffler 			list_len = apos[0] * 256 + apos[1];
64239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST");
64339beb93cSSam Leffler 			if (list_len < 2 || list_len > alen - 2) {
64439beb93cSSam Leffler 				wpa_printf(MSG_WARNING, "EAP-SIM: Invalid "
64539beb93cSSam Leffler 					   "AT_VERSION_LIST (list_len=%lu "
64639beb93cSSam Leffler 					   "attr_len=%lu)",
64739beb93cSSam Leffler 					   (unsigned long) list_len,
64839beb93cSSam Leffler 					   (unsigned long) alen);
64939beb93cSSam Leffler 				return -1;
65039beb93cSSam Leffler 			}
65139beb93cSSam Leffler 			attr->version_list = apos + 2;
65239beb93cSSam Leffler 			attr->version_list_len = list_len;
65339beb93cSSam Leffler 			break;
65439beb93cSSam Leffler 		case EAP_SIM_AT_SELECTED_VERSION:
65539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION");
65639beb93cSSam Leffler 			if (alen != 2) {
65739beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
65839beb93cSSam Leffler 					   "AT_SELECTED_VERSION length %lu",
65939beb93cSSam Leffler 					   (unsigned long) alen);
66039beb93cSSam Leffler 				return -1;
66139beb93cSSam Leffler 			}
66239beb93cSSam Leffler 			attr->selected_version = apos[0] * 256 + apos[1];
66339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION "
66439beb93cSSam Leffler 				   "%d", attr->selected_version);
66539beb93cSSam Leffler 			break;
66639beb93cSSam Leffler 		case EAP_SIM_AT_FULLAUTH_ID_REQ:
66739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_FULLAUTH_ID_REQ");
66839beb93cSSam Leffler 			attr->id_req = FULLAUTH_ID;
66939beb93cSSam Leffler 			break;
67039beb93cSSam Leffler 		case EAP_SIM_AT_COUNTER:
67139beb93cSSam Leffler 			if (!encr) {
67239beb93cSSam Leffler 				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
67339beb93cSSam Leffler 					   "AT_COUNTER");
67439beb93cSSam Leffler 				return -1;
67539beb93cSSam Leffler 			}
67639beb93cSSam Leffler 			if (alen != 2) {
67739beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
67839beb93cSSam Leffler 					   "AT_COUNTER (alen=%lu)",
67939beb93cSSam Leffler 					   (unsigned long) alen);
68039beb93cSSam Leffler 				return -1;
68139beb93cSSam Leffler 			}
68239beb93cSSam Leffler 			attr->counter = apos[0] * 256 + apos[1];
68339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d",
68439beb93cSSam Leffler 				   attr->counter);
68539beb93cSSam Leffler 			break;
68639beb93cSSam Leffler 		case EAP_SIM_AT_COUNTER_TOO_SMALL:
68739beb93cSSam Leffler 			if (!encr) {
68839beb93cSSam Leffler 				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
68939beb93cSSam Leffler 					   "AT_COUNTER_TOO_SMALL");
69039beb93cSSam Leffler 				return -1;
69139beb93cSSam Leffler 			}
69239beb93cSSam Leffler 			if (alen != 2) {
69339beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
69439beb93cSSam Leffler 					   "AT_COUNTER_TOO_SMALL (alen=%lu)",
69539beb93cSSam Leffler 					   (unsigned long) alen);
69639beb93cSSam Leffler 				return -1;
69739beb93cSSam Leffler 			}
69839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
69939beb93cSSam Leffler 				   "AT_COUNTER_TOO_SMALL");
70039beb93cSSam Leffler 			attr->counter_too_small = 1;
70139beb93cSSam Leffler 			break;
70239beb93cSSam Leffler 		case EAP_SIM_AT_NONCE_S:
70339beb93cSSam Leffler 			if (!encr) {
70439beb93cSSam Leffler 				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
70539beb93cSSam Leffler 					   "AT_NONCE_S");
70639beb93cSSam Leffler 				return -1;
70739beb93cSSam Leffler 			}
70839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
70939beb93cSSam Leffler 				   "AT_NONCE_S");
71039beb93cSSam Leffler 			if (alen != 2 + EAP_SIM_NONCE_S_LEN) {
71139beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
71239beb93cSSam Leffler 					   "AT_NONCE_S (alen=%lu)",
71339beb93cSSam Leffler 					   (unsigned long) alen);
71439beb93cSSam Leffler 				return -1;
71539beb93cSSam Leffler 			}
71639beb93cSSam Leffler 			attr->nonce_s = apos + 2;
71739beb93cSSam Leffler 			break;
71839beb93cSSam Leffler 		case EAP_SIM_AT_CLIENT_ERROR_CODE:
71939beb93cSSam Leffler 			if (alen != 2) {
72039beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
72139beb93cSSam Leffler 					   "AT_CLIENT_ERROR_CODE length %lu",
72239beb93cSSam Leffler 					   (unsigned long) alen);
72339beb93cSSam Leffler 				return -1;
72439beb93cSSam Leffler 			}
72539beb93cSSam Leffler 			attr->client_error_code = apos[0] * 256 + apos[1];
72639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_CLIENT_ERROR_CODE "
72739beb93cSSam Leffler 				   "%d", attr->client_error_code);
72839beb93cSSam Leffler 			break;
72939beb93cSSam Leffler 		case EAP_SIM_AT_IV:
73039beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IV");
73139beb93cSSam Leffler 			if (alen != 2 + EAP_SIM_MAC_LEN) {
73239beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_IV "
73339beb93cSSam Leffler 					   "length %lu", (unsigned long) alen);
73439beb93cSSam Leffler 				return -1;
73539beb93cSSam Leffler 			}
73639beb93cSSam Leffler 			attr->iv = apos + 2;
73739beb93cSSam Leffler 			break;
73839beb93cSSam Leffler 		case EAP_SIM_AT_ENCR_DATA:
73939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ENCR_DATA");
74039beb93cSSam Leffler 			attr->encr_data = apos + 2;
74139beb93cSSam Leffler 			attr->encr_data_len = alen - 2;
74239beb93cSSam Leffler 			if (attr->encr_data_len % 16) {
74339beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
74439beb93cSSam Leffler 					   "AT_ENCR_DATA length %lu",
74539beb93cSSam Leffler 					   (unsigned long)
74639beb93cSSam Leffler 					   attr->encr_data_len);
74739beb93cSSam Leffler 				return -1;
74839beb93cSSam Leffler 			}
74939beb93cSSam Leffler 			break;
75039beb93cSSam Leffler 		case EAP_SIM_AT_NEXT_PSEUDONYM:
75139beb93cSSam Leffler 			if (!encr) {
75239beb93cSSam Leffler 				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
75339beb93cSSam Leffler 					   "AT_NEXT_PSEUDONYM");
75439beb93cSSam Leffler 				return -1;
75539beb93cSSam Leffler 			}
75639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
75739beb93cSSam Leffler 				   "AT_NEXT_PSEUDONYM");
75839beb93cSSam Leffler 			plen = apos[0] * 256 + apos[1];
75939beb93cSSam Leffler 			if (plen > alen - 2) {
76039beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
76139beb93cSSam Leffler 					   " AT_NEXT_PSEUDONYM (actual"
76239beb93cSSam Leffler 					   " len %lu, attr len %lu)",
76339beb93cSSam Leffler 					   (unsigned long) plen,
76439beb93cSSam Leffler 					   (unsigned long) alen);
76539beb93cSSam Leffler 				return -1;
76639beb93cSSam Leffler 			}
76739beb93cSSam Leffler 			attr->next_pseudonym = pos + 4;
76839beb93cSSam Leffler 			attr->next_pseudonym_len = plen;
76939beb93cSSam Leffler 			break;
77039beb93cSSam Leffler 		case EAP_SIM_AT_NEXT_REAUTH_ID:
77139beb93cSSam Leffler 			if (!encr) {
77239beb93cSSam Leffler 				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
77339beb93cSSam Leffler 					   "AT_NEXT_REAUTH_ID");
77439beb93cSSam Leffler 				return -1;
77539beb93cSSam Leffler 			}
77639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
77739beb93cSSam Leffler 				   "AT_NEXT_REAUTH_ID");
77839beb93cSSam Leffler 			plen = apos[0] * 256 + apos[1];
77939beb93cSSam Leffler 			if (plen > alen - 2) {
78039beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
78139beb93cSSam Leffler 					   " AT_NEXT_REAUTH_ID (actual"
78239beb93cSSam Leffler 					   " len %lu, attr len %lu)",
78339beb93cSSam Leffler 					   (unsigned long) plen,
78439beb93cSSam Leffler 					   (unsigned long) alen);
78539beb93cSSam Leffler 				return -1;
78639beb93cSSam Leffler 			}
78739beb93cSSam Leffler 			attr->next_reauth_id = pos + 4;
78839beb93cSSam Leffler 			attr->next_reauth_id_len = plen;
78939beb93cSSam Leffler 			break;
79039beb93cSSam Leffler 		case EAP_SIM_AT_RES:
79139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RES");
79239beb93cSSam Leffler 			attr->res_len_bits = WPA_GET_BE16(apos);
79339beb93cSSam Leffler 			apos += 2;
79439beb93cSSam Leffler 			alen -= 2;
79539beb93cSSam Leffler 			if (!aka || alen < EAP_AKA_MIN_RES_LEN ||
79639beb93cSSam Leffler 			    alen > EAP_AKA_MAX_RES_LEN) {
79739beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RES "
79839beb93cSSam Leffler 					   "(len %lu)",
79939beb93cSSam Leffler 					   (unsigned long) alen);
80039beb93cSSam Leffler 				return -1;
80139beb93cSSam Leffler 			}
80239beb93cSSam Leffler 			attr->res = apos;
80339beb93cSSam Leffler 			attr->res_len = alen;
80439beb93cSSam Leffler 			break;
80539beb93cSSam Leffler 		case EAP_SIM_AT_AUTS:
80639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTS");
80739beb93cSSam Leffler 			if (!aka) {
80839beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "EAP-SIM: "
80939beb93cSSam Leffler 					   "Unexpected AT_AUTS");
81039beb93cSSam Leffler 				return -1;
81139beb93cSSam Leffler 			}
81239beb93cSSam Leffler 			if (alen != EAP_AKA_AUTS_LEN) {
81339beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTS"
81439beb93cSSam Leffler 					   " (len %lu)",
81539beb93cSSam Leffler 					   (unsigned long) alen);
81639beb93cSSam Leffler 				return -1;
81739beb93cSSam Leffler 			}
81839beb93cSSam Leffler 			attr->auts = apos;
81939beb93cSSam Leffler 			break;
82039beb93cSSam Leffler 		case EAP_SIM_AT_CHECKCODE:
82139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_CHECKCODE");
82239beb93cSSam Leffler 			if (!aka) {
82339beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "EAP-SIM: "
82439beb93cSSam Leffler 					   "Unexpected AT_CHECKCODE");
82539beb93cSSam Leffler 				return -1;
82639beb93cSSam Leffler 			}
82739beb93cSSam Leffler 			apos += 2;
82839beb93cSSam Leffler 			alen -= 2;
82939beb93cSSam Leffler 			if (alen != 0 && alen != EAP_AKA_CHECKCODE_LEN &&
83039beb93cSSam Leffler 			    alen != EAP_AKA_PRIME_CHECKCODE_LEN) {
83139beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-AKA: Invalid "
83239beb93cSSam Leffler 					   "AT_CHECKCODE (len %lu)",
83339beb93cSSam Leffler 					   (unsigned long) alen);
83439beb93cSSam Leffler 				return -1;
83539beb93cSSam Leffler 			}
83639beb93cSSam Leffler 			attr->checkcode = apos;
83739beb93cSSam Leffler 			attr->checkcode_len = alen;
83839beb93cSSam Leffler 			break;
83939beb93cSSam Leffler 		case EAP_SIM_AT_RESULT_IND:
84039beb93cSSam Leffler 			if (encr) {
84139beb93cSSam Leffler 				wpa_printf(MSG_ERROR, "EAP-SIM: Encrypted "
84239beb93cSSam Leffler 					   "AT_RESULT_IND");
84339beb93cSSam Leffler 				return -1;
84439beb93cSSam Leffler 			}
84539beb93cSSam Leffler 			if (alen != 2) {
84639beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
84739beb93cSSam Leffler 					   "AT_RESULT_IND (alen=%lu)",
84839beb93cSSam Leffler 					   (unsigned long) alen);
84939beb93cSSam Leffler 				return -1;
85039beb93cSSam Leffler 			}
85139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RESULT_IND");
85239beb93cSSam Leffler 			attr->result_ind = 1;
85339beb93cSSam Leffler 			break;
854e28a4053SRui Paulo #if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
85539beb93cSSam Leffler 		case EAP_SIM_AT_KDF_INPUT:
85639beb93cSSam Leffler 			if (aka != 2) {
85739beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
85839beb93cSSam Leffler 					   "AT_KDF_INPUT");
85939beb93cSSam Leffler 				return -1;
86039beb93cSSam Leffler 			}
86139beb93cSSam Leffler 
86239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF_INPUT");
86339beb93cSSam Leffler 			plen = WPA_GET_BE16(apos);
86439beb93cSSam Leffler 			apos += 2;
86539beb93cSSam Leffler 			alen -= 2;
86639beb93cSSam Leffler 			if (plen > alen) {
86739beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
86839beb93cSSam Leffler 					   "AT_KDF_INPUT (Actual Length %lu, "
86939beb93cSSam Leffler 					   "remaining length %lu)",
87039beb93cSSam Leffler 					   (unsigned long) plen,
87139beb93cSSam Leffler 					   (unsigned long) alen);
87239beb93cSSam Leffler 				return -1;
87339beb93cSSam Leffler 			}
87439beb93cSSam Leffler 			attr->kdf_input = apos;
87539beb93cSSam Leffler 			attr->kdf_input_len = plen;
87639beb93cSSam Leffler 			break;
87739beb93cSSam Leffler 		case EAP_SIM_AT_KDF:
87839beb93cSSam Leffler 			if (aka != 2) {
87939beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
88039beb93cSSam Leffler 					   "AT_KDF");
88139beb93cSSam Leffler 				return -1;
88239beb93cSSam Leffler 			}
88339beb93cSSam Leffler 
88439beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF");
88539beb93cSSam Leffler 			if (alen != 2) {
88639beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
88739beb93cSSam Leffler 					   "AT_KDF (len %lu)",
88839beb93cSSam Leffler 					   (unsigned long) alen);
88939beb93cSSam Leffler 				return -1;
89039beb93cSSam Leffler 			}
89139beb93cSSam Leffler 			if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) {
89239beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "EAP-AKA': Too many "
89339beb93cSSam Leffler 					   "AT_KDF attributes - ignore this");
8945b9c547cSRui Paulo 				break;
89539beb93cSSam Leffler 			}
89639beb93cSSam Leffler 			attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos);
89739beb93cSSam Leffler 			attr->kdf_count++;
89839beb93cSSam Leffler 			break;
89939beb93cSSam Leffler 		case EAP_SIM_AT_BIDDING:
90039beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_BIDDING");
90139beb93cSSam Leffler 			if (alen != 2) {
90239beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-AKA: Invalid "
90339beb93cSSam Leffler 					   "AT_BIDDING (len %lu)",
90439beb93cSSam Leffler 					   (unsigned long) alen);
90539beb93cSSam Leffler 				return -1;
90639beb93cSSam Leffler 			}
90739beb93cSSam Leffler 			attr->bidding = apos;
90839beb93cSSam Leffler 			break;
909e28a4053SRui Paulo #endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
91039beb93cSSam Leffler 		default:
91139beb93cSSam Leffler 			if (pos[0] < 128) {
91239beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized "
91339beb93cSSam Leffler 					   "non-skippable attribute %d",
91439beb93cSSam Leffler 					   pos[0]);
91539beb93cSSam Leffler 				return -1;
91639beb93cSSam Leffler 			}
91739beb93cSSam Leffler 
91839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized skippable"
91939beb93cSSam Leffler 				   " attribute %d ignored", pos[0]);
92039beb93cSSam Leffler 			break;
92139beb93cSSam Leffler 		}
92239beb93cSSam Leffler 
92339beb93cSSam Leffler 		pos += pos[1] * 4;
92439beb93cSSam Leffler 	}
92539beb93cSSam Leffler 
92639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-SIM: Attributes parsed successfully "
92739beb93cSSam Leffler 		   "(aka=%d encr=%d)", aka, encr);
92839beb93cSSam Leffler 
92939beb93cSSam Leffler 	return 0;
93039beb93cSSam Leffler }
93139beb93cSSam Leffler 
93239beb93cSSam Leffler 
eap_sim_parse_encr(const u8 * k_encr,const u8 * encr_data,size_t encr_data_len,const u8 * iv,struct eap_sim_attrs * attr,int aka)93339beb93cSSam Leffler u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data,
93439beb93cSSam Leffler 			size_t encr_data_len, const u8 *iv,
93539beb93cSSam Leffler 			struct eap_sim_attrs *attr, int aka)
93639beb93cSSam Leffler {
93739beb93cSSam Leffler 	u8 *decrypted;
93839beb93cSSam Leffler 
93939beb93cSSam Leffler 	if (!iv) {
94039beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV");
94139beb93cSSam Leffler 		return NULL;
94239beb93cSSam Leffler 	}
94339beb93cSSam Leffler 
94485732ac8SCy Schubert 	decrypted = os_memdup(encr_data, encr_data_len);
94539beb93cSSam Leffler 	if (decrypted == NULL)
94639beb93cSSam Leffler 		return NULL;
94739beb93cSSam Leffler 
948206b73d0SCy Schubert #ifdef TEST_FUZZ
949206b73d0SCy Schubert 		wpa_printf(MSG_INFO,
950206b73d0SCy Schubert 			   "TEST: Skip AES-128-CBC decryption for fuzz testing");
951206b73d0SCy Schubert #else /* TEST_FUZZ */
95239beb93cSSam Leffler 	if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) {
95339beb93cSSam Leffler 		os_free(decrypted);
95439beb93cSSam Leffler 		return NULL;
95539beb93cSSam Leffler 	}
956206b73d0SCy Schubert #endif /* TEST_FUZZ */
95739beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA",
95839beb93cSSam Leffler 		    decrypted, encr_data_len);
95939beb93cSSam Leffler 
96039beb93cSSam Leffler 	if (eap_sim_parse_attr(decrypted, decrypted + encr_data_len, attr,
96139beb93cSSam Leffler 			       aka, 1)) {
96239beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse "
96339beb93cSSam Leffler 			   "decrypted AT_ENCR_DATA");
96439beb93cSSam Leffler 		os_free(decrypted);
96539beb93cSSam Leffler 		return NULL;
96639beb93cSSam Leffler 	}
96739beb93cSSam Leffler 
96839beb93cSSam Leffler 	return decrypted;
96939beb93cSSam Leffler }
97039beb93cSSam Leffler 
97139beb93cSSam Leffler 
97239beb93cSSam Leffler #define EAP_SIM_INIT_LEN 128
97339beb93cSSam Leffler 
97439beb93cSSam Leffler struct eap_sim_msg {
97539beb93cSSam Leffler 	struct wpabuf *buf;
97639beb93cSSam Leffler 	size_t mac, iv, encr; /* index from buf */
97739beb93cSSam Leffler };
97839beb93cSSam Leffler 
97939beb93cSSam Leffler 
eap_sim_msg_init(int code,int id,int type,int subtype)98039beb93cSSam Leffler struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype)
98139beb93cSSam Leffler {
98239beb93cSSam Leffler 	struct eap_sim_msg *msg;
98339beb93cSSam Leffler 	struct eap_hdr *eap;
98439beb93cSSam Leffler 	u8 *pos;
98539beb93cSSam Leffler 
98639beb93cSSam Leffler 	msg = os_zalloc(sizeof(*msg));
98739beb93cSSam Leffler 	if (msg == NULL)
98839beb93cSSam Leffler 		return NULL;
98939beb93cSSam Leffler 
99039beb93cSSam Leffler 	msg->buf = wpabuf_alloc(EAP_SIM_INIT_LEN);
99139beb93cSSam Leffler 	if (msg->buf == NULL) {
99239beb93cSSam Leffler 		os_free(msg);
99339beb93cSSam Leffler 		return NULL;
99439beb93cSSam Leffler 	}
99539beb93cSSam Leffler 	eap = wpabuf_put(msg->buf, sizeof(*eap));
99639beb93cSSam Leffler 	eap->code = code;
99739beb93cSSam Leffler 	eap->identifier = id;
99839beb93cSSam Leffler 
99939beb93cSSam Leffler 	pos = wpabuf_put(msg->buf, 4);
100039beb93cSSam Leffler 	*pos++ = type;
100139beb93cSSam Leffler 	*pos++ = subtype;
100239beb93cSSam Leffler 	*pos++ = 0; /* Reserved */
100339beb93cSSam Leffler 	*pos++ = 0; /* Reserved */
100439beb93cSSam Leffler 
100539beb93cSSam Leffler 	return msg;
100639beb93cSSam Leffler }
100739beb93cSSam Leffler 
100839beb93cSSam Leffler 
eap_sim_msg_finish(struct eap_sim_msg * msg,int type,const u8 * k_aut,const u8 * extra,size_t extra_len)10095b9c547cSRui Paulo struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, int type,
10105b9c547cSRui Paulo 				   const u8 *k_aut,
101139beb93cSSam Leffler 				   const u8 *extra, size_t extra_len)
101239beb93cSSam Leffler {
101339beb93cSSam Leffler 	struct eap_hdr *eap;
101439beb93cSSam Leffler 	struct wpabuf *buf;
101539beb93cSSam Leffler 
101639beb93cSSam Leffler 	if (msg == NULL)
101739beb93cSSam Leffler 		return NULL;
101839beb93cSSam Leffler 
101939beb93cSSam Leffler 	eap = wpabuf_mhead(msg->buf);
102039beb93cSSam Leffler 	eap->length = host_to_be16(wpabuf_len(msg->buf));
102139beb93cSSam Leffler 
1022e28a4053SRui Paulo #if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
10235b9c547cSRui Paulo 	if (k_aut && msg->mac && type == EAP_TYPE_AKA_PRIME) {
102439beb93cSSam Leffler 		eap_sim_add_mac_sha256(k_aut, (u8 *) wpabuf_head(msg->buf),
102539beb93cSSam Leffler 				       wpabuf_len(msg->buf),
102639beb93cSSam Leffler 				       (u8 *) wpabuf_mhead(msg->buf) +
102739beb93cSSam Leffler 				       msg->mac, extra, extra_len);
102839beb93cSSam Leffler 	} else
1029e28a4053SRui Paulo #endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
103039beb93cSSam Leffler 	if (k_aut && msg->mac) {
103139beb93cSSam Leffler 		eap_sim_add_mac(k_aut, (u8 *) wpabuf_head(msg->buf),
103239beb93cSSam Leffler 				wpabuf_len(msg->buf),
103339beb93cSSam Leffler 				(u8 *) wpabuf_mhead(msg->buf) + msg->mac,
103439beb93cSSam Leffler 				extra, extra_len);
103539beb93cSSam Leffler 	}
103639beb93cSSam Leffler 
103739beb93cSSam Leffler 	buf = msg->buf;
103839beb93cSSam Leffler 	os_free(msg);
103939beb93cSSam Leffler 	return buf;
104039beb93cSSam Leffler }
104139beb93cSSam Leffler 
104239beb93cSSam Leffler 
eap_sim_msg_free(struct eap_sim_msg * msg)104339beb93cSSam Leffler void eap_sim_msg_free(struct eap_sim_msg *msg)
104439beb93cSSam Leffler {
104539beb93cSSam Leffler 	if (msg) {
104639beb93cSSam Leffler 		wpabuf_free(msg->buf);
104739beb93cSSam Leffler 		os_free(msg);
104839beb93cSSam Leffler 	}
104939beb93cSSam Leffler }
105039beb93cSSam Leffler 
105139beb93cSSam Leffler 
eap_sim_msg_add_full(struct eap_sim_msg * msg,u8 attr,const u8 * data,size_t len)105239beb93cSSam Leffler u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
105339beb93cSSam Leffler 			  const u8 *data, size_t len)
105439beb93cSSam Leffler {
105539beb93cSSam Leffler 	int attr_len = 2 + len;
105639beb93cSSam Leffler 	int pad_len;
105739beb93cSSam Leffler 	u8 *start;
105839beb93cSSam Leffler 
105939beb93cSSam Leffler 	if (msg == NULL)
106039beb93cSSam Leffler 		return NULL;
106139beb93cSSam Leffler 
106239beb93cSSam Leffler 	pad_len = (4 - attr_len % 4) % 4;
106339beb93cSSam Leffler 	attr_len += pad_len;
106439beb93cSSam Leffler 	if (wpabuf_resize(&msg->buf, attr_len))
106539beb93cSSam Leffler 		return NULL;
106639beb93cSSam Leffler 	start = wpabuf_put(msg->buf, 0);
106739beb93cSSam Leffler 	wpabuf_put_u8(msg->buf, attr);
106839beb93cSSam Leffler 	wpabuf_put_u8(msg->buf, attr_len / 4);
106939beb93cSSam Leffler 	wpabuf_put_data(msg->buf, data, len);
107039beb93cSSam Leffler 	if (pad_len)
107139beb93cSSam Leffler 		os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len);
107239beb93cSSam Leffler 	return start;
107339beb93cSSam Leffler }
107439beb93cSSam Leffler 
107539beb93cSSam Leffler 
eap_sim_msg_add(struct eap_sim_msg * msg,u8 attr,u16 value,const u8 * data,size_t len)107639beb93cSSam Leffler u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value,
107739beb93cSSam Leffler 		     const u8 *data, size_t len)
107839beb93cSSam Leffler {
107939beb93cSSam Leffler 	int attr_len = 4 + len;
108039beb93cSSam Leffler 	int pad_len;
108139beb93cSSam Leffler 	u8 *start;
108239beb93cSSam Leffler 
108339beb93cSSam Leffler 	if (msg == NULL)
108439beb93cSSam Leffler 		return NULL;
108539beb93cSSam Leffler 
108639beb93cSSam Leffler 	pad_len = (4 - attr_len % 4) % 4;
108739beb93cSSam Leffler 	attr_len += pad_len;
108839beb93cSSam Leffler 	if (wpabuf_resize(&msg->buf, attr_len))
108939beb93cSSam Leffler 		return NULL;
109039beb93cSSam Leffler 	start = wpabuf_put(msg->buf, 0);
109139beb93cSSam Leffler 	wpabuf_put_u8(msg->buf, attr);
109239beb93cSSam Leffler 	wpabuf_put_u8(msg->buf, attr_len / 4);
109339beb93cSSam Leffler 	wpabuf_put_be16(msg->buf, value);
109439beb93cSSam Leffler 	if (data)
109539beb93cSSam Leffler 		wpabuf_put_data(msg->buf, data, len);
109639beb93cSSam Leffler 	else
109739beb93cSSam Leffler 		wpabuf_put(msg->buf, len);
109839beb93cSSam Leffler 	if (pad_len)
109939beb93cSSam Leffler 		os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len);
110039beb93cSSam Leffler 	return start;
110139beb93cSSam Leffler }
110239beb93cSSam Leffler 
110339beb93cSSam Leffler 
eap_sim_msg_add_mac(struct eap_sim_msg * msg,u8 attr)110439beb93cSSam Leffler u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr)
110539beb93cSSam Leffler {
110639beb93cSSam Leffler 	u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN);
110739beb93cSSam Leffler 	if (pos)
110839beb93cSSam Leffler 		msg->mac = (pos - wpabuf_head_u8(msg->buf)) + 4;
110939beb93cSSam Leffler 	return pos;
111039beb93cSSam Leffler }
111139beb93cSSam Leffler 
111239beb93cSSam Leffler 
eap_sim_msg_add_encr_start(struct eap_sim_msg * msg,u8 attr_iv,u8 attr_encr)111339beb93cSSam Leffler int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
111439beb93cSSam Leffler 			       u8 attr_encr)
111539beb93cSSam Leffler {
111639beb93cSSam Leffler 	u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN);
111739beb93cSSam Leffler 	if (pos == NULL)
111839beb93cSSam Leffler 		return -1;
111939beb93cSSam Leffler 	msg->iv = (pos - wpabuf_head_u8(msg->buf)) + 4;
1120f05cddf9SRui Paulo 	if (random_get_bytes(wpabuf_mhead_u8(msg->buf) + msg->iv,
112139beb93cSSam Leffler 			     EAP_SIM_IV_LEN)) {
112239beb93cSSam Leffler 		msg->iv = 0;
112339beb93cSSam Leffler 		return -1;
112439beb93cSSam Leffler 	}
112539beb93cSSam Leffler 
112639beb93cSSam Leffler 	pos = eap_sim_msg_add(msg, attr_encr, 0, NULL, 0);
112739beb93cSSam Leffler 	if (pos == NULL) {
112839beb93cSSam Leffler 		msg->iv = 0;
112939beb93cSSam Leffler 		return -1;
113039beb93cSSam Leffler 	}
113139beb93cSSam Leffler 	msg->encr = pos - wpabuf_head_u8(msg->buf);
113239beb93cSSam Leffler 
113339beb93cSSam Leffler 	return 0;
113439beb93cSSam Leffler }
113539beb93cSSam Leffler 
113639beb93cSSam Leffler 
eap_sim_msg_add_encr_end(struct eap_sim_msg * msg,u8 * k_encr,int attr_pad)113739beb93cSSam Leffler int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad)
113839beb93cSSam Leffler {
113939beb93cSSam Leffler 	size_t encr_len;
114039beb93cSSam Leffler 
114139beb93cSSam Leffler 	if (msg == NULL || k_encr == NULL || msg->iv == 0 || msg->encr == 0)
114239beb93cSSam Leffler 		return -1;
114339beb93cSSam Leffler 
114439beb93cSSam Leffler 	encr_len = wpabuf_len(msg->buf) - msg->encr - 4;
114539beb93cSSam Leffler 	if (encr_len % 16) {
114639beb93cSSam Leffler 		u8 *pos;
114739beb93cSSam Leffler 		int pad_len = 16 - (encr_len % 16);
114839beb93cSSam Leffler 		if (pad_len < 4) {
114939beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-SIM: "
115039beb93cSSam Leffler 				   "eap_sim_msg_add_encr_end - invalid pad_len"
115139beb93cSSam Leffler 				   " %d", pad_len);
115239beb93cSSam Leffler 			return -1;
115339beb93cSSam Leffler 		}
115439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   *AT_PADDING");
115539beb93cSSam Leffler 		pos = eap_sim_msg_add(msg, attr_pad, 0, NULL, pad_len - 4);
115639beb93cSSam Leffler 		if (pos == NULL)
115739beb93cSSam Leffler 			return -1;
115839beb93cSSam Leffler 		os_memset(pos + 4, 0, pad_len - 4);
115939beb93cSSam Leffler 		encr_len += pad_len;
116039beb93cSSam Leffler 	}
116139beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   (AT_ENCR_DATA data len %lu)",
116239beb93cSSam Leffler 		   (unsigned long) encr_len);
116339beb93cSSam Leffler 	wpabuf_mhead_u8(msg->buf)[msg->encr + 1] = encr_len / 4 + 1;
116439beb93cSSam Leffler 	return aes_128_cbc_encrypt(k_encr, wpabuf_head_u8(msg->buf) + msg->iv,
116539beb93cSSam Leffler 				   wpabuf_mhead_u8(msg->buf) + msg->encr + 4,
116639beb93cSSam Leffler 				   encr_len);
116739beb93cSSam Leffler }
116839beb93cSSam Leffler 
116939beb93cSSam Leffler 
eap_sim_report_notification(void * msg_ctx,int notification,int aka)117039beb93cSSam Leffler void eap_sim_report_notification(void *msg_ctx, int notification, int aka)
117139beb93cSSam Leffler {
117239beb93cSSam Leffler #ifndef CONFIG_NO_STDOUT_DEBUG
117339beb93cSSam Leffler 	const char *type = aka ? "AKA" : "SIM";
117439beb93cSSam Leffler #endif /* CONFIG_NO_STDOUT_DEBUG */
117539beb93cSSam Leffler 
117639beb93cSSam Leffler 	switch (notification) {
117739beb93cSSam Leffler 	case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH:
117839beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-%s: General failure "
117939beb93cSSam Leffler 			   "notification (after authentication)", type);
118039beb93cSSam Leffler 		break;
118139beb93cSSam Leffler 	case EAP_SIM_TEMPORARILY_DENIED:
118239beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
118339beb93cSSam Leffler 			   "User has been temporarily denied access to the "
118439beb93cSSam Leffler 			   "requested service", type);
118539beb93cSSam Leffler 		break;
118639beb93cSSam Leffler 	case EAP_SIM_NOT_SUBSCRIBED:
118739beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
118839beb93cSSam Leffler 			   "User has not subscribed to the requested service",
118939beb93cSSam Leffler 			   type);
119039beb93cSSam Leffler 		break;
119139beb93cSSam Leffler 	case EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH:
119239beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-%s: General failure "
119339beb93cSSam Leffler 			   "notification (before authentication)", type);
119439beb93cSSam Leffler 		break;
119539beb93cSSam Leffler 	case EAP_SIM_SUCCESS:
119639beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-%s: Successful authentication "
119739beb93cSSam Leffler 			   "notification", type);
119839beb93cSSam Leffler 		break;
119939beb93cSSam Leffler 	default:
120039beb93cSSam Leffler 		if (notification >= 32768) {
120139beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-%s: Unrecognized "
120239beb93cSSam Leffler 				   "non-failure notification %d",
120339beb93cSSam Leffler 				   type, notification);
120439beb93cSSam Leffler 		} else {
120539beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-%s: Unrecognized "
120639beb93cSSam Leffler 				   "failure notification %d",
120739beb93cSSam Leffler 				   type, notification);
120839beb93cSSam Leffler 		}
120939beb93cSSam Leffler 	}
121039beb93cSSam Leffler }
1211206b73d0SCy Schubert 
1212206b73d0SCy Schubert 
get_last_char(const u8 * val,size_t len,char c)1213*c1d255d3SCy Schubert static const u8 * get_last_char(const u8 *val, size_t len, char c)
1214*c1d255d3SCy Schubert {
1215*c1d255d3SCy Schubert 	while (len > 0) {
1216*c1d255d3SCy Schubert 		const u8 *pos = &val[len - 1];
1217*c1d255d3SCy Schubert 
1218*c1d255d3SCy Schubert 		if (*pos == (u8) c)
1219*c1d255d3SCy Schubert 			return pos;
1220*c1d255d3SCy Schubert 		len--;
1221*c1d255d3SCy Schubert 	}
1222*c1d255d3SCy Schubert 
1223*c1d255d3SCy Schubert 	return NULL;
1224*c1d255d3SCy Schubert }
1225*c1d255d3SCy Schubert 
1226*c1d255d3SCy Schubert 
eap_sim_anonymous_username(const u8 * id,size_t id_len)1227206b73d0SCy Schubert int eap_sim_anonymous_username(const u8 *id, size_t id_len)
1228206b73d0SCy Schubert {
1229206b73d0SCy Schubert 	static const char *anonymous_id_prefix = "anonymous@";
1230*c1d255d3SCy Schubert 	const u8 *decorated;
1231206b73d0SCy Schubert 	size_t anonymous_id_len = os_strlen(anonymous_id_prefix);
1232206b73d0SCy Schubert 
1233206b73d0SCy Schubert 	if (id_len > anonymous_id_len &&
1234206b73d0SCy Schubert 	    os_memcmp(id, anonymous_id_prefix, anonymous_id_len) == 0)
1235206b73d0SCy Schubert 		return 1; /* 'anonymous@realm' */
1236206b73d0SCy Schubert 
1237*c1d255d3SCy Schubert 	if (id_len > anonymous_id_len + 1 &&
1238*c1d255d3SCy Schubert 	    os_memcmp(id + 1, anonymous_id_prefix, anonymous_id_len) == 0)
1239*c1d255d3SCy Schubert 		return 1; /* 'Xanonymous@realm' where X is an EAP method code */
1240*c1d255d3SCy Schubert 
1241206b73d0SCy Schubert 	if (id_len > 1 && id[0] == '@')
1242206b73d0SCy Schubert 		return 1; /* '@realm' */
1243206b73d0SCy Schubert 
1244*c1d255d3SCy Schubert 	/* RFC 7542 decorated username, for example:
1245*c1d255d3SCy Schubert 	 * homerealm.example.org!anonymous@otherrealm.example.net */
1246*c1d255d3SCy Schubert 	decorated = get_last_char(id, id_len, '!');
1247*c1d255d3SCy Schubert 	if (decorated) {
1248*c1d255d3SCy Schubert 		decorated++;
1249*c1d255d3SCy Schubert 		return eap_sim_anonymous_username(decorated,
1250*c1d255d3SCy Schubert 						  id + id_len - decorated);
1251*c1d255d3SCy Schubert 	}
1252*c1d255d3SCy Schubert 
1253206b73d0SCy Schubert 	return 0;
1254206b73d0SCy Schubert }
1255