]>
Commit | Line | Data |
---|---|---|
6fc6879b | 1 | /* |
f54e2c34 | 2 | * EAP peer/server: EAP-SIM/AKA/AKA' shared routines |
6fc6879b JM |
3 | * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> |
4 | * | |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
6fc6879b JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | ||
11 | #include "common.h" | |
6fc6879b | 12 | #include "wpabuf.h" |
03da66bd JM |
13 | #include "crypto/aes_wrap.h" |
14 | #include "crypto/crypto.h" | |
15 | #include "crypto/sha1.h" | |
16 | #include "crypto/sha256.h" | |
3642c431 | 17 | #include "crypto/random.h" |
03da66bd | 18 | #include "eap_common/eap_defs.h" |
6fc6879b JM |
19 | #include "eap_common/eap_sim_common.h" |
20 | ||
21 | ||
22 | static int eap_sim_prf(const u8 *key, u8 *x, size_t xlen) | |
23 | { | |
24 | return fips186_2_prf(key, EAP_SIM_MK_LEN, x, xlen); | |
25 | } | |
26 | ||
27 | ||
28 | void eap_sim_derive_mk(const u8 *identity, size_t identity_len, | |
29 | const u8 *nonce_mt, u16 selected_version, | |
30 | const u8 *ver_list, size_t ver_list_len, | |
31 | int num_chal, const u8 *kc, u8 *mk) | |
32 | { | |
33 | u8 sel_ver[2]; | |
34 | const unsigned char *addr[5]; | |
35 | size_t len[5]; | |
36 | ||
37 | addr[0] = identity; | |
38 | len[0] = identity_len; | |
39 | addr[1] = kc; | |
40 | len[1] = num_chal * EAP_SIM_KC_LEN; | |
41 | addr[2] = nonce_mt; | |
42 | len[2] = EAP_SIM_NONCE_MT_LEN; | |
43 | addr[3] = ver_list; | |
44 | len[3] = ver_list_len; | |
45 | addr[4] = sel_ver; | |
46 | len[4] = 2; | |
47 | ||
48 | WPA_PUT_BE16(sel_ver, selected_version); | |
49 | ||
50 | /* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */ | |
51 | sha1_vector(5, addr, len, mk); | |
52 | wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN); | |
53 | } | |
54 | ||
55 | ||
56 | void eap_aka_derive_mk(const u8 *identity, size_t identity_len, | |
57 | const u8 *ik, const u8 *ck, u8 *mk) | |
58 | { | |
59 | const u8 *addr[3]; | |
60 | size_t len[3]; | |
61 | ||
62 | addr[0] = identity; | |
63 | len[0] = identity_len; | |
64 | addr[1] = ik; | |
65 | len[1] = EAP_AKA_IK_LEN; | |
66 | addr[2] = ck; | |
67 | len[2] = EAP_AKA_CK_LEN; | |
68 | ||
69 | /* MK = SHA1(Identity|IK|CK) */ | |
70 | sha1_vector(3, addr, len, mk); | |
71 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", ik, EAP_AKA_IK_LEN); | |
72 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", ck, EAP_AKA_CK_LEN); | |
73 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: MK", mk, EAP_SIM_MK_LEN); | |
74 | } | |
75 | ||
76 | ||
77 | int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk, u8 *emsk) | |
78 | { | |
79 | u8 buf[EAP_SIM_K_ENCR_LEN + EAP_SIM_K_AUT_LEN + | |
80 | EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN], *pos; | |
81 | if (eap_sim_prf(mk, buf, sizeof(buf)) < 0) { | |
82 | wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys"); | |
83 | return -1; | |
84 | } | |
85 | pos = buf; | |
86 | os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN); | |
87 | pos += EAP_SIM_K_ENCR_LEN; | |
88 | os_memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN); | |
89 | pos += EAP_SIM_K_AUT_LEN; | |
90 | os_memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN); | |
91 | pos += EAP_SIM_KEYING_DATA_LEN; | |
92 | os_memcpy(emsk, pos, EAP_EMSK_LEN); | |
93 | ||
94 | wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr", | |
95 | k_encr, EAP_SIM_K_ENCR_LEN); | |
96 | wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut", | |
97 | k_aut, EAP_SIM_K_AUT_LEN); | |
98 | wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material (MSK)", | |
99 | msk, EAP_SIM_KEYING_DATA_LEN); | |
100 | wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN); | |
101 | os_memset(buf, 0, sizeof(buf)); | |
102 | ||
103 | return 0; | |
104 | } | |
105 | ||
106 | ||
107 | int eap_sim_derive_keys_reauth(u16 _counter, | |
108 | const u8 *identity, size_t identity_len, | |
109 | const u8 *nonce_s, const u8 *mk, u8 *msk, | |
110 | u8 *emsk) | |
111 | { | |
112 | u8 xkey[SHA1_MAC_LEN]; | |
113 | u8 buf[EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN + 32]; | |
114 | u8 counter[2]; | |
115 | const u8 *addr[4]; | |
116 | size_t len[4]; | |
117 | ||
118 | while (identity_len > 0 && identity[identity_len - 1] == 0) { | |
119 | wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop null " | |
120 | "character from the end of identity"); | |
121 | identity_len--; | |
122 | } | |
123 | addr[0] = identity; | |
124 | len[0] = identity_len; | |
125 | addr[1] = counter; | |
126 | len[1] = 2; | |
127 | addr[2] = nonce_s; | |
128 | len[2] = EAP_SIM_NONCE_S_LEN; | |
129 | addr[3] = mk; | |
130 | len[3] = EAP_SIM_MK_LEN; | |
131 | ||
132 | WPA_PUT_BE16(counter, _counter); | |
133 | ||
134 | wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth"); | |
135 | wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", | |
136 | identity, identity_len); | |
137 | wpa_hexdump(MSG_DEBUG, "EAP-SIM: counter", counter, 2); | |
138 | wpa_hexdump(MSG_DEBUG, "EAP-SIM: NONCE_S", nonce_s, | |
139 | EAP_SIM_NONCE_S_LEN); | |
140 | wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN); | |
141 | ||
142 | /* XKEY' = SHA1(Identity|counter|NONCE_S|MK) */ | |
143 | sha1_vector(4, addr, len, xkey); | |
144 | wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN); | |
145 | ||
146 | if (eap_sim_prf(xkey, buf, sizeof(buf)) < 0) { | |
147 | wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys"); | |
148 | return -1; | |
149 | } | |
150 | if (msk) { | |
151 | os_memcpy(msk, buf, EAP_SIM_KEYING_DATA_LEN); | |
152 | wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material (MSK)", | |
153 | msk, EAP_SIM_KEYING_DATA_LEN); | |
154 | } | |
155 | if (emsk) { | |
156 | os_memcpy(emsk, buf + EAP_SIM_KEYING_DATA_LEN, EAP_EMSK_LEN); | |
157 | wpa_hexdump(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN); | |
158 | } | |
159 | os_memset(buf, 0, sizeof(buf)); | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
164 | ||
165 | int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req, | |
166 | const u8 *mac, const u8 *extra, size_t extra_len) | |
167 | { | |
168 | unsigned char hmac[SHA1_MAC_LEN]; | |
169 | const u8 *addr[2]; | |
170 | size_t len[2]; | |
171 | u8 *tmp; | |
172 | ||
173 | if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN || | |
174 | mac < wpabuf_head_u8(req) || | |
175 | mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN) | |
176 | return -1; | |
177 | ||
178 | tmp = os_malloc(wpabuf_len(req)); | |
179 | if (tmp == NULL) | |
180 | return -1; | |
181 | ||
182 | addr[0] = tmp; | |
183 | len[0] = wpabuf_len(req); | |
184 | addr[1] = extra; | |
185 | len[1] = extra_len; | |
186 | ||
187 | /* HMAC-SHA1-128 */ | |
188 | os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req)); | |
189 | os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN); | |
190 | wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg", | |
191 | tmp, wpabuf_len(req)); | |
192 | wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - extra data", | |
193 | extra, extra_len); | |
194 | wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Verify MAC - K_aut", | |
195 | k_aut, EAP_SIM_K_AUT_LEN); | |
196 | hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); | |
197 | wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC: MAC", | |
198 | hmac, EAP_SIM_MAC_LEN); | |
199 | os_free(tmp); | |
200 | ||
201 | return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; | |
202 | } | |
203 | ||
204 | ||
205 | void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac, | |
206 | const u8 *extra, size_t extra_len) | |
207 | { | |
208 | unsigned char hmac[SHA1_MAC_LEN]; | |
209 | const u8 *addr[2]; | |
210 | size_t len[2]; | |
211 | ||
212 | addr[0] = msg; | |
213 | len[0] = msg_len; | |
214 | addr[1] = extra; | |
215 | len[1] = extra_len; | |
216 | ||
217 | /* HMAC-SHA1-128 */ | |
218 | os_memset(mac, 0, EAP_SIM_MAC_LEN); | |
219 | wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - msg", msg, msg_len); | |
220 | wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - extra data", | |
221 | extra, extra_len); | |
222 | wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Add MAC - K_aut", | |
223 | k_aut, EAP_SIM_K_AUT_LEN); | |
224 | hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); | |
225 | os_memcpy(mac, hmac, EAP_SIM_MAC_LEN); | |
226 | wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC: MAC", | |
227 | mac, EAP_SIM_MAC_LEN); | |
228 | } | |
229 | ||
230 | ||
1e5839e0 | 231 | #if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME) |
9881795e JM |
232 | static void prf_prime(const u8 *k, const char *seed1, |
233 | const u8 *seed2, size_t seed2_len, | |
234 | const u8 *seed3, size_t seed3_len, | |
235 | u8 *res, size_t res_len) | |
236 | { | |
237 | const u8 *addr[5]; | |
238 | size_t len[5]; | |
239 | u8 hash[SHA256_MAC_LEN]; | |
240 | u8 iter; | |
241 | ||
242 | /* | |
243 | * PRF'(K,S) = T1 | T2 | T3 | T4 | ... | |
244 | * T1 = HMAC-SHA-256 (K, S | 0x01) | |
245 | * T2 = HMAC-SHA-256 (K, T1 | S | 0x02) | |
246 | * T3 = HMAC-SHA-256 (K, T2 | S | 0x03) | |
247 | * T4 = HMAC-SHA-256 (K, T3 | S | 0x04) | |
248 | * ... | |
249 | */ | |
250 | ||
251 | addr[0] = hash; | |
252 | len[0] = 0; | |
253 | addr[1] = (const u8 *) seed1; | |
254 | len[1] = os_strlen(seed1); | |
255 | addr[2] = seed2; | |
256 | len[2] = seed2_len; | |
257 | addr[3] = seed3; | |
258 | len[3] = seed3_len; | |
259 | addr[4] = &iter; | |
260 | len[4] = 1; | |
261 | ||
262 | iter = 0; | |
263 | while (res_len) { | |
264 | size_t hlen; | |
265 | iter++; | |
266 | hmac_sha256_vector(k, 32, 5, addr, len, hash); | |
267 | len[0] = SHA256_MAC_LEN; | |
268 | hlen = res_len > SHA256_MAC_LEN ? SHA256_MAC_LEN : res_len; | |
269 | os_memcpy(res, hash, hlen); | |
270 | res += hlen; | |
271 | res_len -= hlen; | |
272 | } | |
273 | } | |
274 | ||
275 | ||
276 | void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len, | |
277 | const u8 *ik, const u8 *ck, u8 *k_encr, | |
278 | u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk) | |
279 | { | |
280 | u8 key[EAP_AKA_IK_LEN + EAP_AKA_CK_LEN]; | |
281 | u8 keys[EAP_SIM_K_ENCR_LEN + EAP_AKA_PRIME_K_AUT_LEN + | |
282 | EAP_AKA_PRIME_K_RE_LEN + EAP_MSK_LEN + EAP_EMSK_LEN]; | |
283 | u8 *pos; | |
284 | ||
285 | /* | |
286 | * MK = PRF'(IK'|CK',"EAP-AKA'"|Identity) | |
287 | * K_encr = MK[0..127] | |
288 | * K_aut = MK[128..383] | |
289 | * K_re = MK[384..639] | |
290 | * MSK = MK[640..1151] | |
291 | * EMSK = MK[1152..1663] | |
292 | */ | |
293 | ||
294 | os_memcpy(key, ik, EAP_AKA_IK_LEN); | |
295 | os_memcpy(key + EAP_AKA_IK_LEN, ck, EAP_AKA_CK_LEN); | |
296 | ||
297 | prf_prime(key, "EAP-AKA'", identity, identity_len, NULL, 0, | |
298 | keys, sizeof(keys)); | |
299 | ||
300 | pos = keys; | |
301 | os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN); | |
302 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_encr", | |
303 | k_encr, EAP_SIM_K_ENCR_LEN); | |
304 | pos += EAP_SIM_K_ENCR_LEN; | |
305 | ||
306 | os_memcpy(k_aut, pos, EAP_AKA_PRIME_K_AUT_LEN); | |
307 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_aut", | |
308 | k_aut, EAP_AKA_PRIME_K_AUT_LEN); | |
309 | pos += EAP_AKA_PRIME_K_AUT_LEN; | |
310 | ||
311 | os_memcpy(k_re, pos, EAP_AKA_PRIME_K_RE_LEN); | |
312 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_re", | |
313 | k_re, EAP_AKA_PRIME_K_RE_LEN); | |
314 | pos += EAP_AKA_PRIME_K_RE_LEN; | |
315 | ||
316 | os_memcpy(msk, pos, EAP_MSK_LEN); | |
317 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN); | |
318 | pos += EAP_MSK_LEN; | |
319 | ||
320 | os_memcpy(emsk, pos, EAP_EMSK_LEN); | |
321 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN); | |
322 | } | |
323 | ||
324 | ||
325 | int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter, | |
326 | const u8 *identity, size_t identity_len, | |
327 | const u8 *nonce_s, u8 *msk, u8 *emsk) | |
328 | { | |
329 | u8 seed3[2 + EAP_SIM_NONCE_S_LEN]; | |
330 | u8 keys[EAP_MSK_LEN + EAP_EMSK_LEN]; | |
331 | u8 *pos; | |
332 | ||
333 | /* | |
334 | * MK = PRF'(K_re,"EAP-AKA' re-auth"|Identity|counter|NONCE_S) | |
335 | * MSK = MK[0..511] | |
336 | * EMSK = MK[512..1023] | |
337 | */ | |
338 | ||
339 | WPA_PUT_BE16(seed3, counter); | |
340 | os_memcpy(seed3 + 2, nonce_s, EAP_SIM_NONCE_S_LEN); | |
341 | ||
342 | prf_prime(k_re, "EAP-AKA' re-auth", identity, identity_len, | |
343 | seed3, sizeof(seed3), | |
344 | keys, sizeof(keys)); | |
345 | ||
346 | pos = keys; | |
347 | os_memcpy(msk, pos, EAP_MSK_LEN); | |
348 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN); | |
349 | pos += EAP_MSK_LEN; | |
350 | ||
351 | os_memcpy(emsk, pos, EAP_EMSK_LEN); | |
352 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN); | |
353 | ||
354 | os_memset(keys, 0, sizeof(keys)); | |
355 | ||
356 | return 0; | |
357 | } | |
358 | ||
359 | ||
806f8699 JM |
360 | int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req, |
361 | const u8 *mac, const u8 *extra, size_t extra_len) | |
362 | { | |
363 | unsigned char hmac[SHA256_MAC_LEN]; | |
364 | const u8 *addr[2]; | |
365 | size_t len[2]; | |
366 | u8 *tmp; | |
367 | ||
368 | if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN || | |
369 | mac < wpabuf_head_u8(req) || | |
370 | mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN) | |
371 | return -1; | |
372 | ||
373 | tmp = os_malloc(wpabuf_len(req)); | |
374 | if (tmp == NULL) | |
375 | return -1; | |
376 | ||
377 | addr[0] = tmp; | |
378 | len[0] = wpabuf_len(req); | |
379 | addr[1] = extra; | |
380 | len[1] = extra_len; | |
381 | ||
382 | /* HMAC-SHA-256-128 */ | |
383 | os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req)); | |
384 | os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN); | |
385 | wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - msg", | |
386 | tmp, wpabuf_len(req)); | |
387 | wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - extra data", | |
388 | extra, extra_len); | |
389 | wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Verify MAC - K_aut", | |
390 | k_aut, EAP_AKA_PRIME_K_AUT_LEN); | |
391 | hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac); | |
392 | wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC: MAC", | |
393 | hmac, EAP_SIM_MAC_LEN); | |
394 | os_free(tmp); | |
395 | ||
396 | return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; | |
397 | } | |
398 | ||
399 | ||
400 | void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len, | |
401 | u8 *mac, const u8 *extra, size_t extra_len) | |
402 | { | |
403 | unsigned char hmac[SHA256_MAC_LEN]; | |
404 | const u8 *addr[2]; | |
405 | size_t len[2]; | |
406 | ||
407 | addr[0] = msg; | |
408 | len[0] = msg_len; | |
409 | addr[1] = extra; | |
410 | len[1] = extra_len; | |
411 | ||
412 | /* HMAC-SHA-256-128 */ | |
413 | os_memset(mac, 0, EAP_SIM_MAC_LEN); | |
414 | wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - msg", msg, msg_len); | |
415 | wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - extra data", | |
416 | extra, extra_len); | |
417 | wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Add MAC - K_aut", | |
418 | k_aut, EAP_AKA_PRIME_K_AUT_LEN); | |
419 | hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac); | |
420 | os_memcpy(mac, hmac, EAP_SIM_MAC_LEN); | |
421 | wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC: MAC", | |
422 | mac, EAP_SIM_MAC_LEN); | |
423 | } | |
a478ef0d JM |
424 | |
425 | ||
426 | void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak, | |
427 | const u8 *network_name, | |
428 | size_t network_name_len) | |
429 | { | |
430 | u8 key[EAP_AKA_CK_LEN + EAP_AKA_IK_LEN]; | |
431 | u8 hash[SHA256_MAC_LEN]; | |
432 | const u8 *addr[5]; | |
433 | size_t len[5]; | |
434 | u8 fc; | |
435 | u8 l0[2], l1[2]; | |
436 | ||
437 | /* 3GPP TS 33.402 V8.0.0 | |
438 | * (CK', IK') = F(CK, IK, <access network identity>) | |
439 | */ | |
440 | /* TODO: CK', IK' generation should really be moved into the actual | |
441 | * AKA procedure with network name passed in there and option to use | |
442 | * AMF separation bit = 1 (3GPP TS 33.401). */ | |
443 | ||
8de5048e | 444 | /* Change Request 33.402 CR 0033 to version 8.1.1 from |
a478ef0d JM |
445 | * 3GPP TSG-SA WG3 Meeting #53 in September 2008: |
446 | * | |
447 | * CK' || IK' = HMAC-SHA-256(Key, S) | |
448 | * S = FC || P0 || L0 || P1 || L1 || ... || Pn || Ln | |
449 | * Key = CK || IK | |
450 | * FC = 0x20 | |
451 | * P0 = access network identity (3GPP TS 24.302) | |
452 | * L0 = length of acceess network identity (2 octets, big endian) | |
453 | * P1 = SQN xor AK (if AK is not used, AK is treaded as 000..0 | |
454 | * L1 = 0x00 0x06 | |
455 | */ | |
456 | ||
457 | fc = 0x20; | |
458 | ||
459 | wpa_printf(MSG_DEBUG, "EAP-AKA': Derive (CK',IK') from (CK,IK)"); | |
460 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK", ck, EAP_AKA_CK_LEN); | |
461 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK", ik, EAP_AKA_IK_LEN); | |
462 | wpa_printf(MSG_DEBUG, "EAP-AKA': FC = 0x%x", fc); | |
463 | wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': P0 = Access network identity", | |
464 | network_name, network_name_len); | |
465 | wpa_hexdump(MSG_DEBUG, "EAP-AKA': P1 = SQN xor AK", sqn_ak, 6); | |
466 | ||
467 | os_memcpy(key, ck, EAP_AKA_CK_LEN); | |
468 | os_memcpy(key + EAP_AKA_CK_LEN, ik, EAP_AKA_IK_LEN); | |
469 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': Key = CK || IK", | |
470 | key, sizeof(key)); | |
471 | ||
472 | addr[0] = &fc; | |
473 | len[0] = 1; | |
474 | addr[1] = network_name; | |
475 | len[1] = network_name_len; | |
476 | WPA_PUT_BE16(l0, network_name_len); | |
477 | addr[2] = l0; | |
478 | len[2] = 2; | |
479 | addr[3] = sqn_ak; | |
480 | len[3] = 6; | |
481 | WPA_PUT_BE16(l1, 6); | |
482 | addr[4] = l1; | |
483 | len[4] = 2; | |
484 | ||
485 | hmac_sha256_vector(key, sizeof(key), 5, addr, len, hash); | |
486 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': KDF output (CK' || IK')", | |
487 | hash, sizeof(hash)); | |
488 | ||
489 | os_memcpy(ck, hash, EAP_AKA_CK_LEN); | |
490 | os_memcpy(ik, hash + EAP_AKA_CK_LEN, EAP_AKA_IK_LEN); | |
491 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK'", ck, EAP_AKA_CK_LEN); | |
492 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK'", ik, EAP_AKA_IK_LEN); | |
493 | } | |
1e5839e0 | 494 | #endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ |
806f8699 JM |
495 | |
496 | ||
6fc6879b JM |
497 | int eap_sim_parse_attr(const u8 *start, const u8 *end, |
498 | struct eap_sim_attrs *attr, int aka, int encr) | |
499 | { | |
500 | const u8 *pos = start, *apos; | |
501 | size_t alen, plen, i, list_len; | |
502 | ||
503 | os_memset(attr, 0, sizeof(*attr)); | |
504 | attr->id_req = NO_ID_REQ; | |
505 | attr->notification = -1; | |
506 | attr->counter = -1; | |
507 | attr->selected_version = -1; | |
508 | attr->client_error_code = -1; | |
509 | ||
510 | while (pos < end) { | |
511 | if (pos + 2 > end) { | |
512 | wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow(1)"); | |
513 | return -1; | |
514 | } | |
515 | wpa_printf(MSG_MSGDUMP, "EAP-SIM: Attribute: Type=%d Len=%d", | |
516 | pos[0], pos[1] * 4); | |
517 | if (pos + pos[1] * 4 > end) { | |
518 | wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow " | |
519 | "(pos=%p len=%d end=%p)", | |
520 | pos, pos[1] * 4, end); | |
521 | return -1; | |
522 | } | |
523 | if (pos[1] == 0) { | |
524 | wpa_printf(MSG_INFO, "EAP-SIM: Attribute underflow"); | |
525 | return -1; | |
526 | } | |
527 | apos = pos + 2; | |
528 | alen = pos[1] * 4 - 2; | |
529 | wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Attribute data", | |
530 | apos, alen); | |
531 | ||
532 | switch (pos[0]) { | |
533 | case EAP_SIM_AT_RAND: | |
534 | wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RAND"); | |
535 | apos += 2; | |
536 | alen -= 2; | |
537 | if ((!aka && (alen % GSM_RAND_LEN)) || | |
538 | (aka && alen != EAP_AKA_RAND_LEN)) { | |
539 | wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND" | |
540 | " (len %lu)", | |
541 | (unsigned long) alen); | |
542 | return -1; | |
543 | } | |
544 | attr->rand = apos; | |
545 | attr->num_chal = alen / GSM_RAND_LEN; | |
546 | break; | |
547 | case EAP_SIM_AT_AUTN: | |
548 | wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTN"); | |
549 | if (!aka) { | |
550 | wpa_printf(MSG_DEBUG, "EAP-SIM: " | |
551 | "Unexpected AT_AUTN"); | |
552 | return -1; | |
553 | } | |
554 | apos += 2; | |
555 | alen -= 2; | |
556 | if (alen != EAP_AKA_AUTN_LEN) { | |
557 | wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN" | |
558 | " (len %lu)", | |
559 | (unsigned long) alen); | |
560 | return -1; | |
561 | } | |
562 | attr->autn = apos; | |
563 | break; | |
564 | case EAP_SIM_AT_PADDING: | |
565 | if (!encr) { | |
566 | wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " | |
567 | "AT_PADDING"); | |
568 | return -1; | |
569 | } | |
570 | wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_PADDING"); | |
571 | for (i = 2; i < alen; i++) { | |
572 | if (apos[i] != 0) { | |
573 | wpa_printf(MSG_INFO, "EAP-SIM: (encr) " | |
574 | "AT_PADDING used a non-zero" | |
575 | " padding byte"); | |
576 | wpa_hexdump(MSG_DEBUG, "EAP-SIM: " | |
577 | "(encr) padding bytes", | |
578 | apos + 2, alen - 2); | |
579 | return -1; | |
580 | } | |
581 | } | |
582 | break; | |
583 | case EAP_SIM_AT_NONCE_MT: | |
584 | wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NONCE_MT"); | |
585 | if (alen != 2 + EAP_SIM_NONCE_MT_LEN) { | |
586 | wpa_printf(MSG_INFO, "EAP-SIM: Invalid " | |
587 | "AT_NONCE_MT length"); | |
588 | return -1; | |
589 | } | |
590 | attr->nonce_mt = apos + 2; | |
591 | break; | |
592 | case EAP_SIM_AT_PERMANENT_ID_REQ: | |
593 | wpa_printf(MSG_DEBUG, "EAP-SIM: AT_PERMANENT_ID_REQ"); | |
594 | attr->id_req = PERMANENT_ID; | |
595 | break; | |
596 | case EAP_SIM_AT_MAC: | |
597 | wpa_printf(MSG_DEBUG, "EAP-SIM: AT_MAC"); | |
598 | if (alen != 2 + EAP_SIM_MAC_LEN) { | |
599 | wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_MAC " | |
600 | "length"); | |
601 | return -1; | |
602 | } | |
603 | attr->mac = apos + 2; | |
604 | break; | |
605 | case EAP_SIM_AT_NOTIFICATION: | |
606 | if (alen != 2) { | |
607 | wpa_printf(MSG_INFO, "EAP-SIM: Invalid " | |
608 | "AT_NOTIFICATION length %lu", | |
609 | (unsigned long) alen); | |
610 | return -1; | |
611 | } | |
612 | attr->notification = apos[0] * 256 + apos[1]; | |
613 | wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NOTIFICATION %d", | |
614 | attr->notification); | |
615 | break; | |
616 | case EAP_SIM_AT_ANY_ID_REQ: | |
617 | wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ANY_ID_REQ"); | |
618 | attr->id_req = ANY_ID; | |
619 | break; | |
620 | case EAP_SIM_AT_IDENTITY: | |
621 | wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY"); | |
b8ab6249 JM |
622 | plen = WPA_GET_BE16(apos); |
623 | apos += 2; | |
624 | alen -= 2; | |
625 | if (plen > alen) { | |
626 | wpa_printf(MSG_INFO, "EAP-SIM: Invalid " | |
627 | "AT_IDENTITY (Actual Length %lu, " | |
628 | "remaining length %lu)", | |
629 | (unsigned long) plen, | |
630 | (unsigned long) alen); | |
631 | return -1; | |
632 | } | |
633 | ||
634 | attr->identity = apos; | |
635 | attr->identity_len = plen; | |
6fc6879b JM |
636 | break; |
637 | case EAP_SIM_AT_VERSION_LIST: | |
638 | if (aka) { | |
639 | wpa_printf(MSG_DEBUG, "EAP-AKA: " | |
640 | "Unexpected AT_VERSION_LIST"); | |
641 | return -1; | |
642 | } | |
643 | list_len = apos[0] * 256 + apos[1]; | |
644 | wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST"); | |
645 | if (list_len < 2 || list_len > alen - 2) { | |
646 | wpa_printf(MSG_WARNING, "EAP-SIM: Invalid " | |
647 | "AT_VERSION_LIST (list_len=%lu " | |
648 | "attr_len=%lu)", | |
649 | (unsigned long) list_len, | |
650 | (unsigned long) alen); | |
651 | return -1; | |
652 | } | |
653 | attr->version_list = apos + 2; | |
654 | attr->version_list_len = list_len; | |
655 | break; | |
656 | case EAP_SIM_AT_SELECTED_VERSION: | |
657 | wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION"); | |
658 | if (alen != 2) { | |
659 | wpa_printf(MSG_INFO, "EAP-SIM: Invalid " | |
660 | "AT_SELECTED_VERSION length %lu", | |
661 | (unsigned long) alen); | |
662 | return -1; | |
663 | } | |
664 | attr->selected_version = apos[0] * 256 + apos[1]; | |
665 | wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION " | |
666 | "%d", attr->selected_version); | |
667 | break; | |
668 | case EAP_SIM_AT_FULLAUTH_ID_REQ: | |
669 | wpa_printf(MSG_DEBUG, "EAP-SIM: AT_FULLAUTH_ID_REQ"); | |
670 | attr->id_req = FULLAUTH_ID; | |
671 | break; | |
672 | case EAP_SIM_AT_COUNTER: | |
673 | if (!encr) { | |
674 | wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " | |
675 | "AT_COUNTER"); | |
676 | return -1; | |
677 | } | |
678 | if (alen != 2) { | |
679 | wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid " | |
680 | "AT_COUNTER (alen=%lu)", | |
681 | (unsigned long) alen); | |
682 | return -1; | |
683 | } | |
684 | attr->counter = apos[0] * 256 + apos[1]; | |
685 | wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d", | |
686 | attr->counter); | |
687 | break; | |
688 | case EAP_SIM_AT_COUNTER_TOO_SMALL: | |
689 | if (!encr) { | |
690 | wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " | |
691 | "AT_COUNTER_TOO_SMALL"); | |
692 | return -1; | |
693 | } | |
694 | if (alen != 2) { | |
695 | wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid " | |
696 | "AT_COUNTER_TOO_SMALL (alen=%lu)", | |
697 | (unsigned long) alen); | |
698 | return -1; | |
699 | } | |
700 | wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " | |
701 | "AT_COUNTER_TOO_SMALL"); | |
702 | attr->counter_too_small = 1; | |
703 | break; | |
704 | case EAP_SIM_AT_NONCE_S: | |
705 | if (!encr) { | |
706 | wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " | |
707 | "AT_NONCE_S"); | |
708 | return -1; | |
709 | } | |
710 | wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " | |
711 | "AT_NONCE_S"); | |
712 | if (alen != 2 + EAP_SIM_NONCE_S_LEN) { | |
713 | wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid " | |
714 | "AT_NONCE_S (alen=%lu)", | |
715 | (unsigned long) alen); | |
716 | return -1; | |
717 | } | |
718 | attr->nonce_s = apos + 2; | |
719 | break; | |
720 | case EAP_SIM_AT_CLIENT_ERROR_CODE: | |
721 | if (alen != 2) { | |
722 | wpa_printf(MSG_INFO, "EAP-SIM: Invalid " | |
723 | "AT_CLIENT_ERROR_CODE length %lu", | |
724 | (unsigned long) alen); | |
725 | return -1; | |
726 | } | |
727 | attr->client_error_code = apos[0] * 256 + apos[1]; | |
728 | wpa_printf(MSG_DEBUG, "EAP-SIM: AT_CLIENT_ERROR_CODE " | |
729 | "%d", attr->client_error_code); | |
730 | break; | |
731 | case EAP_SIM_AT_IV: | |
732 | wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IV"); | |
733 | if (alen != 2 + EAP_SIM_MAC_LEN) { | |
734 | wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_IV " | |
735 | "length %lu", (unsigned long) alen); | |
736 | return -1; | |
737 | } | |
738 | attr->iv = apos + 2; | |
739 | break; | |
740 | case EAP_SIM_AT_ENCR_DATA: | |
741 | wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ENCR_DATA"); | |
742 | attr->encr_data = apos + 2; | |
743 | attr->encr_data_len = alen - 2; | |
744 | if (attr->encr_data_len % 16) { | |
745 | wpa_printf(MSG_INFO, "EAP-SIM: Invalid " | |
746 | "AT_ENCR_DATA length %lu", | |
747 | (unsigned long) | |
748 | attr->encr_data_len); | |
749 | return -1; | |
750 | } | |
751 | break; | |
752 | case EAP_SIM_AT_NEXT_PSEUDONYM: | |
753 | if (!encr) { | |
754 | wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " | |
755 | "AT_NEXT_PSEUDONYM"); | |
756 | return -1; | |
757 | } | |
758 | wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " | |
759 | "AT_NEXT_PSEUDONYM"); | |
760 | plen = apos[0] * 256 + apos[1]; | |
761 | if (plen > alen - 2) { | |
762 | wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid" | |
763 | " AT_NEXT_PSEUDONYM (actual" | |
764 | " len %lu, attr len %lu)", | |
765 | (unsigned long) plen, | |
766 | (unsigned long) alen); | |
767 | return -1; | |
768 | } | |
769 | attr->next_pseudonym = pos + 4; | |
770 | attr->next_pseudonym_len = plen; | |
771 | break; | |
772 | case EAP_SIM_AT_NEXT_REAUTH_ID: | |
773 | if (!encr) { | |
774 | wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " | |
775 | "AT_NEXT_REAUTH_ID"); | |
776 | return -1; | |
777 | } | |
778 | wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " | |
779 | "AT_NEXT_REAUTH_ID"); | |
780 | plen = apos[0] * 256 + apos[1]; | |
781 | if (plen > alen - 2) { | |
782 | wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid" | |
783 | " AT_NEXT_REAUTH_ID (actual" | |
784 | " len %lu, attr len %lu)", | |
785 | (unsigned long) plen, | |
786 | (unsigned long) alen); | |
787 | return -1; | |
788 | } | |
789 | attr->next_reauth_id = pos + 4; | |
790 | attr->next_reauth_id_len = plen; | |
791 | break; | |
792 | case EAP_SIM_AT_RES: | |
793 | wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RES"); | |
04a5bad6 | 794 | attr->res_len_bits = WPA_GET_BE16(apos); |
6fc6879b JM |
795 | apos += 2; |
796 | alen -= 2; | |
797 | if (!aka || alen < EAP_AKA_MIN_RES_LEN || | |
798 | alen > EAP_AKA_MAX_RES_LEN) { | |
799 | wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RES " | |
800 | "(len %lu)", | |
801 | (unsigned long) alen); | |
802 | return -1; | |
803 | } | |
804 | attr->res = apos; | |
805 | attr->res_len = alen; | |
806 | break; | |
807 | case EAP_SIM_AT_AUTS: | |
808 | wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTS"); | |
809 | if (!aka) { | |
810 | wpa_printf(MSG_DEBUG, "EAP-SIM: " | |
811 | "Unexpected AT_AUTS"); | |
812 | return -1; | |
813 | } | |
814 | if (alen != EAP_AKA_AUTS_LEN) { | |
815 | wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTS" | |
816 | " (len %lu)", | |
817 | (unsigned long) alen); | |
818 | return -1; | |
819 | } | |
820 | attr->auts = apos; | |
821 | break; | |
822 | case EAP_SIM_AT_CHECKCODE: | |
823 | wpa_printf(MSG_DEBUG, "EAP-AKA: AT_CHECKCODE"); | |
824 | if (!aka) { | |
825 | wpa_printf(MSG_DEBUG, "EAP-SIM: " | |
826 | "Unexpected AT_CHECKCODE"); | |
827 | return -1; | |
828 | } | |
829 | apos += 2; | |
830 | alen -= 2; | |
f54e2c34 JM |
831 | if (alen != 0 && alen != EAP_AKA_CHECKCODE_LEN && |
832 | alen != EAP_AKA_PRIME_CHECKCODE_LEN) { | |
6fc6879b JM |
833 | wpa_printf(MSG_INFO, "EAP-AKA: Invalid " |
834 | "AT_CHECKCODE (len %lu)", | |
835 | (unsigned long) alen); | |
836 | return -1; | |
837 | } | |
838 | attr->checkcode = apos; | |
839 | attr->checkcode_len = alen; | |
840 | break; | |
841 | case EAP_SIM_AT_RESULT_IND: | |
842 | if (encr) { | |
843 | wpa_printf(MSG_ERROR, "EAP-SIM: Encrypted " | |
844 | "AT_RESULT_IND"); | |
845 | return -1; | |
846 | } | |
847 | if (alen != 2) { | |
848 | wpa_printf(MSG_INFO, "EAP-SIM: Invalid " | |
849 | "AT_RESULT_IND (alen=%lu)", | |
850 | (unsigned long) alen); | |
851 | return -1; | |
852 | } | |
853 | wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RESULT_IND"); | |
854 | attr->result_ind = 1; | |
855 | break; | |
1e5839e0 | 856 | #if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME) |
6ec4021c JM |
857 | case EAP_SIM_AT_KDF_INPUT: |
858 | if (aka != 2) { | |
859 | wpa_printf(MSG_INFO, "EAP-AKA: Unexpected " | |
860 | "AT_KDF_INPUT"); | |
861 | return -1; | |
862 | } | |
863 | ||
864 | wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF_INPUT"); | |
865 | plen = WPA_GET_BE16(apos); | |
866 | apos += 2; | |
867 | alen -= 2; | |
868 | if (plen > alen) { | |
869 | wpa_printf(MSG_INFO, "EAP-AKA': Invalid " | |
870 | "AT_KDF_INPUT (Actual Length %lu, " | |
871 | "remaining length %lu)", | |
872 | (unsigned long) plen, | |
873 | (unsigned long) alen); | |
874 | return -1; | |
875 | } | |
876 | attr->kdf_input = apos; | |
877 | attr->kdf_input_len = plen; | |
878 | break; | |
879 | case EAP_SIM_AT_KDF: | |
880 | if (aka != 2) { | |
881 | wpa_printf(MSG_INFO, "EAP-AKA: Unexpected " | |
882 | "AT_KDF"); | |
883 | return -1; | |
884 | } | |
885 | ||
886 | wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF"); | |
887 | if (alen != 2) { | |
888 | wpa_printf(MSG_INFO, "EAP-AKA': Invalid " | |
889 | "AT_KDF (len %lu)", | |
890 | (unsigned long) alen); | |
891 | return -1; | |
892 | } | |
893 | if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) { | |
894 | wpa_printf(MSG_DEBUG, "EAP-AKA': Too many " | |
895 | "AT_KDF attributes - ignore this"); | |
896 | continue; | |
897 | } | |
898 | attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos); | |
899 | attr->kdf_count++; | |
900 | break; | |
01b05694 JM |
901 | case EAP_SIM_AT_BIDDING: |
902 | wpa_printf(MSG_DEBUG, "EAP-AKA: AT_BIDDING"); | |
903 | if (alen != 2) { | |
904 | wpa_printf(MSG_INFO, "EAP-AKA: Invalid " | |
905 | "AT_BIDDING (len %lu)", | |
906 | (unsigned long) alen); | |
907 | return -1; | |
908 | } | |
909 | attr->bidding = apos; | |
910 | break; | |
1e5839e0 | 911 | #endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ |
6fc6879b JM |
912 | default: |
913 | if (pos[0] < 128) { | |
914 | wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized " | |
915 | "non-skippable attribute %d", | |
916 | pos[0]); | |
917 | return -1; | |
918 | } | |
919 | ||
920 | wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized skippable" | |
921 | " attribute %d ignored", pos[0]); | |
922 | break; | |
923 | } | |
924 | ||
925 | pos += pos[1] * 4; | |
926 | } | |
927 | ||
928 | wpa_printf(MSG_DEBUG, "EAP-SIM: Attributes parsed successfully " | |
929 | "(aka=%d encr=%d)", aka, encr); | |
930 | ||
931 | return 0; | |
932 | } | |
933 | ||
934 | ||
935 | u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, | |
936 | size_t encr_data_len, const u8 *iv, | |
937 | struct eap_sim_attrs *attr, int aka) | |
938 | { | |
939 | u8 *decrypted; | |
940 | ||
941 | if (!iv) { | |
942 | wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV"); | |
943 | return NULL; | |
944 | } | |
945 | ||
946 | decrypted = os_malloc(encr_data_len); | |
947 | if (decrypted == NULL) | |
948 | return NULL; | |
949 | os_memcpy(decrypted, encr_data, encr_data_len); | |
950 | ||
951 | if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) { | |
952 | os_free(decrypted); | |
953 | return NULL; | |
954 | } | |
955 | wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA", | |
956 | decrypted, encr_data_len); | |
957 | ||
958 | if (eap_sim_parse_attr(decrypted, decrypted + encr_data_len, attr, | |
959 | aka, 1)) { | |
960 | wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse " | |
961 | "decrypted AT_ENCR_DATA"); | |
962 | os_free(decrypted); | |
963 | return NULL; | |
964 | } | |
965 | ||
966 | return decrypted; | |
967 | } | |
968 | ||
969 | ||
970 | #define EAP_SIM_INIT_LEN 128 | |
971 | ||
972 | struct eap_sim_msg { | |
973 | struct wpabuf *buf; | |
974 | size_t mac, iv, encr; /* index from buf */ | |
806f8699 | 975 | int type; |
6fc6879b JM |
976 | }; |
977 | ||
978 | ||
979 | struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype) | |
980 | { | |
981 | struct eap_sim_msg *msg; | |
982 | struct eap_hdr *eap; | |
983 | u8 *pos; | |
984 | ||
985 | msg = os_zalloc(sizeof(*msg)); | |
986 | if (msg == NULL) | |
987 | return NULL; | |
988 | ||
806f8699 | 989 | msg->type = type; |
6fc6879b JM |
990 | msg->buf = wpabuf_alloc(EAP_SIM_INIT_LEN); |
991 | if (msg->buf == NULL) { | |
992 | os_free(msg); | |
993 | return NULL; | |
994 | } | |
995 | eap = wpabuf_put(msg->buf, sizeof(*eap)); | |
996 | eap->code = code; | |
997 | eap->identifier = id; | |
998 | ||
999 | pos = wpabuf_put(msg->buf, 4); | |
1000 | *pos++ = type; | |
1001 | *pos++ = subtype; | |
1002 | *pos++ = 0; /* Reserved */ | |
1003 | *pos++ = 0; /* Reserved */ | |
1004 | ||
1005 | return msg; | |
1006 | } | |
1007 | ||
1008 | ||
1009 | struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut, | |
1010 | const u8 *extra, size_t extra_len) | |
1011 | { | |
1012 | struct eap_hdr *eap; | |
1013 | struct wpabuf *buf; | |
1014 | ||
1015 | if (msg == NULL) | |
1016 | return NULL; | |
1017 | ||
1018 | eap = wpabuf_mhead(msg->buf); | |
1019 | eap->length = host_to_be16(wpabuf_len(msg->buf)); | |
1020 | ||
1e5839e0 | 1021 | #if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME) |
806f8699 JM |
1022 | if (k_aut && msg->mac && msg->type == EAP_TYPE_AKA_PRIME) { |
1023 | eap_sim_add_mac_sha256(k_aut, (u8 *) wpabuf_head(msg->buf), | |
1024 | wpabuf_len(msg->buf), | |
1025 | (u8 *) wpabuf_mhead(msg->buf) + | |
1026 | msg->mac, extra, extra_len); | |
1027 | } else | |
1e5839e0 | 1028 | #endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ |
6fc6879b JM |
1029 | if (k_aut && msg->mac) { |
1030 | eap_sim_add_mac(k_aut, (u8 *) wpabuf_head(msg->buf), | |
1031 | wpabuf_len(msg->buf), | |
1032 | (u8 *) wpabuf_mhead(msg->buf) + msg->mac, | |
1033 | extra, extra_len); | |
1034 | } | |
1035 | ||
1036 | buf = msg->buf; | |
1037 | os_free(msg); | |
1038 | return buf; | |
1039 | } | |
1040 | ||
1041 | ||
1042 | void eap_sim_msg_free(struct eap_sim_msg *msg) | |
1043 | { | |
1044 | if (msg) { | |
1045 | wpabuf_free(msg->buf); | |
1046 | os_free(msg); | |
1047 | } | |
1048 | } | |
1049 | ||
1050 | ||
1051 | u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr, | |
1052 | const u8 *data, size_t len) | |
1053 | { | |
1054 | int attr_len = 2 + len; | |
1055 | int pad_len; | |
1056 | u8 *start; | |
1057 | ||
1058 | if (msg == NULL) | |
1059 | return NULL; | |
1060 | ||
1061 | pad_len = (4 - attr_len % 4) % 4; | |
1062 | attr_len += pad_len; | |
1063 | if (wpabuf_resize(&msg->buf, attr_len)) | |
1064 | return NULL; | |
1065 | start = wpabuf_put(msg->buf, 0); | |
1066 | wpabuf_put_u8(msg->buf, attr); | |
1067 | wpabuf_put_u8(msg->buf, attr_len / 4); | |
1068 | wpabuf_put_data(msg->buf, data, len); | |
1069 | if (pad_len) | |
1070 | os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len); | |
1071 | return start; | |
1072 | } | |
1073 | ||
1074 | ||
1075 | u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value, | |
1076 | const u8 *data, size_t len) | |
1077 | { | |
1078 | int attr_len = 4 + len; | |
1079 | int pad_len; | |
1080 | u8 *start; | |
1081 | ||
1082 | if (msg == NULL) | |
1083 | return NULL; | |
1084 | ||
1085 | pad_len = (4 - attr_len % 4) % 4; | |
1086 | attr_len += pad_len; | |
1087 | if (wpabuf_resize(&msg->buf, attr_len)) | |
1088 | return NULL; | |
1089 | start = wpabuf_put(msg->buf, 0); | |
1090 | wpabuf_put_u8(msg->buf, attr); | |
1091 | wpabuf_put_u8(msg->buf, attr_len / 4); | |
1092 | wpabuf_put_be16(msg->buf, value); | |
1093 | if (data) | |
1094 | wpabuf_put_data(msg->buf, data, len); | |
1095 | else | |
1096 | wpabuf_put(msg->buf, len); | |
1097 | if (pad_len) | |
1098 | os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len); | |
1099 | return start; | |
1100 | } | |
1101 | ||
1102 | ||
1103 | u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr) | |
1104 | { | |
1105 | u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN); | |
1106 | if (pos) | |
1107 | msg->mac = (pos - wpabuf_head_u8(msg->buf)) + 4; | |
1108 | return pos; | |
1109 | } | |
1110 | ||
1111 | ||
1112 | int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, | |
1113 | u8 attr_encr) | |
1114 | { | |
1115 | u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN); | |
1116 | if (pos == NULL) | |
1117 | return -1; | |
1118 | msg->iv = (pos - wpabuf_head_u8(msg->buf)) + 4; | |
3642c431 JM |
1119 | if (random_get_bytes(wpabuf_mhead_u8(msg->buf) + msg->iv, |
1120 | EAP_SIM_IV_LEN)) { | |
6fc6879b JM |
1121 | msg->iv = 0; |
1122 | return -1; | |
1123 | } | |
1124 | ||
1125 | pos = eap_sim_msg_add(msg, attr_encr, 0, NULL, 0); | |
1126 | if (pos == NULL) { | |
1127 | msg->iv = 0; | |
1128 | return -1; | |
1129 | } | |
1130 | msg->encr = pos - wpabuf_head_u8(msg->buf); | |
1131 | ||
1132 | return 0; | |
1133 | } | |
1134 | ||
1135 | ||
1136 | int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad) | |
1137 | { | |
1138 | size_t encr_len; | |
1139 | ||
1140 | if (msg == NULL || k_encr == NULL || msg->iv == 0 || msg->encr == 0) | |
1141 | return -1; | |
1142 | ||
1143 | encr_len = wpabuf_len(msg->buf) - msg->encr - 4; | |
1144 | if (encr_len % 16) { | |
1145 | u8 *pos; | |
1146 | int pad_len = 16 - (encr_len % 16); | |
1147 | if (pad_len < 4) { | |
1148 | wpa_printf(MSG_WARNING, "EAP-SIM: " | |
1149 | "eap_sim_msg_add_encr_end - invalid pad_len" | |
1150 | " %d", pad_len); | |
1151 | return -1; | |
1152 | } | |
1153 | wpa_printf(MSG_DEBUG, " *AT_PADDING"); | |
1154 | pos = eap_sim_msg_add(msg, attr_pad, 0, NULL, pad_len - 4); | |
1155 | if (pos == NULL) | |
1156 | return -1; | |
1157 | os_memset(pos + 4, 0, pad_len - 4); | |
1158 | encr_len += pad_len; | |
1159 | } | |
1160 | wpa_printf(MSG_DEBUG, " (AT_ENCR_DATA data len %lu)", | |
1161 | (unsigned long) encr_len); | |
1162 | wpabuf_mhead_u8(msg->buf)[msg->encr + 1] = encr_len / 4 + 1; | |
1163 | return aes_128_cbc_encrypt(k_encr, wpabuf_head_u8(msg->buf) + msg->iv, | |
1164 | wpabuf_mhead_u8(msg->buf) + msg->encr + 4, | |
1165 | encr_len); | |
1166 | } | |
1167 | ||
1168 | ||
1169 | void eap_sim_report_notification(void *msg_ctx, int notification, int aka) | |
1170 | { | |
1171 | #ifndef CONFIG_NO_STDOUT_DEBUG | |
1172 | const char *type = aka ? "AKA" : "SIM"; | |
1173 | #endif /* CONFIG_NO_STDOUT_DEBUG */ | |
1174 | ||
1175 | switch (notification) { | |
1176 | case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH: | |
1177 | wpa_printf(MSG_WARNING, "EAP-%s: General failure " | |
1178 | "notification (after authentication)", type); | |
1179 | break; | |
1180 | case EAP_SIM_TEMPORARILY_DENIED: | |
1181 | wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: " | |
1182 | "User has been temporarily denied access to the " | |
1183 | "requested service", type); | |
1184 | break; | |
1185 | case EAP_SIM_NOT_SUBSCRIBED: | |
1186 | wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: " | |
1187 | "User has not subscribed to the requested service", | |
1188 | type); | |
1189 | break; | |
1190 | case EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH: | |
1191 | wpa_printf(MSG_WARNING, "EAP-%s: General failure " | |
1192 | "notification (before authentication)", type); | |
1193 | break; | |
1194 | case EAP_SIM_SUCCESS: | |
1195 | wpa_printf(MSG_INFO, "EAP-%s: Successful authentication " | |
1196 | "notification", type); | |
1197 | break; | |
1198 | default: | |
1199 | if (notification >= 32768) { | |
1200 | wpa_printf(MSG_INFO, "EAP-%s: Unrecognized " | |
1201 | "non-failure notification %d", | |
1202 | type, notification); | |
1203 | } else { | |
1204 | wpa_printf(MSG_WARNING, "EAP-%s: Unrecognized " | |
1205 | "failure notification %d", | |
1206 | type, notification); | |
1207 | } | |
1208 | } | |
1209 | } |