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