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