]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - contrib/wpa_supplicant/eap_sim_common.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / contrib / wpa_supplicant / eap_sim_common.c
1 /*
2  * EAP peer: EAP-SIM/AKA shared routines
3  * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14
15 #include "includes.h"
16
17 #include "common.h"
18 #include "eap_i.h"
19 #include "sha1.h"
20 #include "crypto.h"
21 #include "aes_wrap.h"
22 #include "eap_sim_common.h"
23
24
25 static int eap_sim_prf(const u8 *key, u8 *x, size_t xlen)
26 {
27         return fips186_2_prf(key, EAP_SIM_MK_LEN, x, xlen);
28 }
29
30
31 void eap_sim_derive_mk(const u8 *identity, size_t identity_len,
32                        const u8 *nonce_mt, u16 selected_version,
33                        const u8 *ver_list, size_t ver_list_len,
34                        int num_chal, const u8 *kc, u8 *mk)
35 {
36         u8 sel_ver[2];
37         const unsigned char *addr[5];
38         size_t len[5];
39
40         addr[0] = identity;
41         len[0] = identity_len;
42         addr[1] = kc;
43         len[1] = num_chal * EAP_SIM_KC_LEN;
44         addr[2] = nonce_mt;
45         len[2] = EAP_SIM_NONCE_MT_LEN;
46         addr[3] = ver_list;
47         len[3] = ver_list_len;
48         addr[4] = sel_ver;
49         len[4] = 2;
50
51         WPA_PUT_BE16(sel_ver, selected_version);
52
53         /* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
54         sha1_vector(5, addr, len, mk);
55         wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN);
56 }
57
58
59 void eap_aka_derive_mk(const u8 *identity, size_t identity_len,
60                        const u8 *ik, const u8 *ck, u8 *mk)
61 {
62         const u8 *addr[3];
63         size_t len[3];
64
65         addr[0] = identity;
66         len[0] = identity_len;
67         addr[1] = ik;
68         len[1] = EAP_AKA_IK_LEN;
69         addr[2] = ck;
70         len[2] = EAP_AKA_CK_LEN;
71
72         /* MK = SHA1(Identity|IK|CK) */
73         sha1_vector(3, addr, len, mk);
74         wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", ik, EAP_AKA_IK_LEN);
75         wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", ck, EAP_AKA_CK_LEN);
76         wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: MK", mk, EAP_SIM_MK_LEN);
77 }
78
79
80 int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk, u8 *emsk)
81 {
82         u8 buf[EAP_SIM_K_ENCR_LEN + EAP_SIM_K_AUT_LEN +
83                EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN], *pos;
84         if (eap_sim_prf(mk, buf, sizeof(buf)) < 0) {
85                 wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys");
86                 return -1;
87         }
88         pos = buf;
89         os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN);
90         pos += EAP_SIM_K_ENCR_LEN;
91         os_memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN);
92         pos += EAP_SIM_K_AUT_LEN;
93         os_memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN);
94         pos += EAP_SIM_KEYING_DATA_LEN;
95         os_memcpy(emsk, pos, EAP_EMSK_LEN);
96
97         wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr",
98                         k_encr, EAP_SIM_K_ENCR_LEN);
99         wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut",
100                         k_aut, EAP_SIM_K_AUT_LEN);
101         wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material (MSK)",
102                         msk, EAP_SIM_KEYING_DATA_LEN);
103         wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN);
104         os_memset(buf, 0, sizeof(buf));
105
106         return 0;
107 }
108
109
110 int eap_sim_derive_keys_reauth(u16 _counter,
111                                const u8 *identity, size_t identity_len,
112                                const u8 *nonce_s, const u8 *mk, u8 *msk,
113                                u8 *emsk)
114 {
115         u8 xkey[SHA1_MAC_LEN];
116         u8 buf[EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN + 32];
117         u8 counter[2];
118         const u8 *addr[4];
119         size_t len[4];
120
121         while (identity_len > 0 && identity[identity_len - 1] == 0) {
122                 wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop null "
123                            "character from the end of identity");
124                 identity_len--;
125         }
126         addr[0] = identity;
127         len[0] = identity_len;
128         addr[1] = counter;
129         len[1] = 2;
130         addr[2] = nonce_s;
131         len[2] = EAP_SIM_NONCE_S_LEN;
132         addr[3] = mk;
133         len[3] = EAP_SIM_MK_LEN;
134
135         WPA_PUT_BE16(counter, _counter);
136
137         wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth");
138         wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
139                           identity, identity_len);
140         wpa_hexdump(MSG_DEBUG, "EAP-SIM: counter", counter, 2);
141         wpa_hexdump(MSG_DEBUG, "EAP-SIM: NONCE_S", nonce_s,
142                     EAP_SIM_NONCE_S_LEN);
143         wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN);
144
145         /* XKEY' = SHA1(Identity|counter|NONCE_S|MK) */
146         sha1_vector(4, addr, len, xkey);
147         wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN);
148
149         if (eap_sim_prf(xkey, buf, sizeof(buf)) < 0) {
150                 wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys");
151                 return -1;
152         }
153         if (msk) {
154                 os_memcpy(msk, buf, EAP_SIM_KEYING_DATA_LEN);
155                 wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material (MSK)",
156                             msk, EAP_SIM_KEYING_DATA_LEN);
157         }
158         if (emsk) {
159                 os_memcpy(emsk, buf + EAP_SIM_KEYING_DATA_LEN, EAP_EMSK_LEN);
160                 wpa_hexdump(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN);
161         }
162         os_memset(buf, 0, sizeof(buf));
163
164         return 0;
165 }
166
167
168 int eap_sim_verify_mac(const u8 *k_aut, const u8 *req, size_t req_len,
169                        const u8 *mac, const u8 *extra, size_t extra_len)
170 {
171         unsigned char hmac[SHA1_MAC_LEN];
172         const u8 *addr[2];
173         size_t len[2];
174         u8 *tmp;
175
176         if (mac == NULL || req_len < EAP_SIM_MAC_LEN || mac < req ||
177             mac > req + req_len - EAP_SIM_MAC_LEN)
178                 return -1;
179
180         tmp = os_malloc(req_len);
181         if (tmp == NULL)
182                 return -1;
183
184         addr[0] = tmp;
185         len[0] = req_len;
186         addr[1] = extra;
187         len[1] = extra_len;
188
189         /* HMAC-SHA1-128 */
190         os_memcpy(tmp, req, req_len);
191         os_memset(tmp + (mac - req), 0, EAP_SIM_MAC_LEN);
192         wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg", tmp, req_len);
193         wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - extra data",
194                     extra, extra_len);
195         wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Verify MAC - K_aut",
196                         k_aut, EAP_SIM_K_AUT_LEN);
197         hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
198         wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC: MAC",
199                     hmac, EAP_SIM_MAC_LEN);
200         os_free(tmp);
201
202         return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
203 }
204
205
206 void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac,
207                      const u8 *extra, size_t extra_len)
208 {
209         unsigned char hmac[SHA1_MAC_LEN];
210         const u8 *addr[2];
211         size_t len[2];
212
213         addr[0] = msg;
214         len[0] = msg_len;
215         addr[1] = extra;
216         len[1] = extra_len;
217
218         /* HMAC-SHA1-128 */
219         os_memset(mac, 0, EAP_SIM_MAC_LEN);
220         wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - msg", msg, msg_len);
221         wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - extra data",
222                     extra, extra_len);
223         wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Add MAC - K_aut",
224                         k_aut, EAP_SIM_K_AUT_LEN);
225         hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
226         os_memcpy(mac, hmac, EAP_SIM_MAC_LEN);
227         wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC: MAC",
228                     mac, EAP_SIM_MAC_LEN);
229 }
230
231
232 int eap_sim_parse_attr(const u8 *start, const u8 *end,
233                        struct eap_sim_attrs *attr, int aka, int encr)
234 {
235         const u8 *pos = start, *apos;
236         size_t alen, plen, i, list_len;
237
238         os_memset(attr, 0, sizeof(*attr));
239         attr->id_req = NO_ID_REQ;
240         attr->notification = -1;
241         attr->counter = -1;
242         attr->selected_version = -1;
243         attr->client_error_code = -1;
244
245         while (pos < end) {
246                 if (pos + 2 > end) {
247                         wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow(1)");
248                         return -1;
249                 }
250                 wpa_printf(MSG_MSGDUMP, "EAP-SIM: Attribute: Type=%d Len=%d",
251                            pos[0], pos[1] * 4);
252                 if (pos + pos[1] * 4 > end) {
253                         wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow "
254                                    "(pos=%p len=%d end=%p)",
255                                    pos, pos[1] * 4, end);
256                         return -1;
257                 }
258                 if (pos[1] == 0) {
259                         wpa_printf(MSG_INFO, "EAP-SIM: Attribute underflow");
260                         return -1;
261                 }
262                 apos = pos + 2;
263                 alen = pos[1] * 4 - 2;
264                 wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Attribute data",
265                             apos, alen);
266
267                 switch (pos[0]) {
268                 case EAP_SIM_AT_RAND:
269                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RAND");
270                         apos += 2;
271                         alen -= 2;
272                         if ((!aka && (alen % GSM_RAND_LEN)) ||
273                             (aka && alen != EAP_AKA_RAND_LEN)) {
274                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND"
275                                            " (len %lu)",
276                                            (unsigned long) alen);
277                                 return -1;
278                         }
279                         attr->rand = apos;
280                         attr->num_chal = alen / GSM_RAND_LEN;
281                         break;
282                 case EAP_SIM_AT_AUTN:
283                         wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTN");
284                         if (!aka) {
285                                 wpa_printf(MSG_DEBUG, "EAP-SIM: "
286                                            "Unexpected AT_AUTN");
287                                 return -1;
288                         }
289                         apos += 2;
290                         alen -= 2;
291                         if (alen != EAP_AKA_AUTN_LEN) {
292                                 wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN"
293                                            " (len %lu)",
294                                            (unsigned long) alen);
295                                 return -1;
296                         }
297                         attr->autn = apos;
298                         break;
299                 case EAP_SIM_AT_PADDING:
300                         if (!encr) {
301                                 wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
302                                            "AT_PADDING");
303                                 return -1;
304                         }
305                         wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_PADDING");
306                         for (i = 2; i < alen; i++) {
307                                 if (apos[i] != 0) {
308                                         wpa_printf(MSG_INFO, "EAP-SIM: (encr) "
309                                                    "AT_PADDING used a non-zero"
310                                                    " padding byte");
311                                         wpa_hexdump(MSG_DEBUG, "EAP-SIM: "
312                                                     "(encr) padding bytes",
313                                                     apos + 2, alen - 2);
314                                         return -1;
315                                 }
316                         }
317                         break;
318                 case EAP_SIM_AT_NONCE_MT:
319                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NONCE_MT");
320                         if (alen != 2 + EAP_SIM_NONCE_MT_LEN) {
321                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
322                                            "AT_NONCE_MT length");
323                                 return -1;
324                         }
325                         attr->nonce_mt = apos + 2;
326                         break;
327                 case EAP_SIM_AT_PERMANENT_ID_REQ:
328                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_PERMANENT_ID_REQ");
329                         attr->id_req = PERMANENT_ID;
330                         break;
331                 case EAP_SIM_AT_MAC:
332                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_MAC");
333                         if (alen != 2 + EAP_SIM_MAC_LEN) {
334                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_MAC "
335                                            "length");
336                                 return -1;
337                         }
338                         attr->mac = apos + 2;
339                         break;
340                 case EAP_SIM_AT_NOTIFICATION:
341                         if (alen != 2) {
342                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
343                                            "AT_NOTIFICATION length %lu",
344                                            (unsigned long) alen);
345                                 return -1;
346                         }
347                         attr->notification = apos[0] * 256 + apos[1];
348                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NOTIFICATION %d",
349                                    attr->notification);
350                         break;
351                 case EAP_SIM_AT_ANY_ID_REQ:
352                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ANY_ID_REQ");
353                         attr->id_req = ANY_ID;
354                         break;
355                 case EAP_SIM_AT_IDENTITY:
356                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY");
357                         attr->identity = apos + 2;
358                         attr->identity_len = alen - 2;
359                         break;
360                 case EAP_SIM_AT_VERSION_LIST:
361                         if (aka) {
362                                 wpa_printf(MSG_DEBUG, "EAP-AKA: "
363                                            "Unexpected AT_VERSION_LIST");
364                                 return -1;
365                         }
366                         list_len = apos[0] * 256 + apos[1];
367                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST");
368                         if (list_len < 2 || list_len > alen - 2) {
369                                 wpa_printf(MSG_WARNING, "EAP-SIM: Invalid "
370                                            "AT_VERSION_LIST (list_len=%lu "
371                                            "attr_len=%lu)",
372                                            (unsigned long) list_len,
373                                            (unsigned long) alen);
374                                 return -1;
375                         }
376                         attr->version_list = apos + 2;
377                         attr->version_list_len = list_len;
378                         break;
379                 case EAP_SIM_AT_SELECTED_VERSION:
380                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION");
381                         if (alen != 2) {
382                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
383                                            "AT_SELECTED_VERSION length %lu",
384                                            (unsigned long) alen);
385                                 return -1;
386                         }
387                         attr->selected_version = apos[0] * 256 + apos[1];
388                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION "
389                                    "%d", attr->selected_version);
390                         break;
391                 case EAP_SIM_AT_FULLAUTH_ID_REQ:
392                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_FULLAUTH_ID_REQ");
393                         attr->id_req = FULLAUTH_ID;
394                         break;
395                 case EAP_SIM_AT_COUNTER:
396                         if (!encr) {
397                                 wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
398                                            "AT_COUNTER");
399                                 return -1;
400                         }
401                         if (alen != 2) {
402                                 wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
403                                            "AT_COUNTER (alen=%lu)",
404                                            (unsigned long) alen);
405                                 return -1;
406                         }
407                         attr->counter = apos[0] * 256 + apos[1];
408                         wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d",
409                                    attr->counter);
410                         break;
411                 case EAP_SIM_AT_COUNTER_TOO_SMALL:
412                         if (!encr) {
413                                 wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
414                                            "AT_COUNTER_TOO_SMALL");
415                                 return -1;
416                         }
417                         if (alen != 2) {
418                                 wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
419                                            "AT_COUNTER_TOO_SMALL (alen=%lu)",
420                                            (unsigned long) alen);
421                                 return -1;
422                         }
423                         wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
424                                    "AT_COUNTER_TOO_SMALL");
425                         attr->counter_too_small = 1;
426                         break;
427                 case EAP_SIM_AT_NONCE_S:
428                         if (!encr) {
429                                 wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
430                                            "AT_NONCE_S");
431                                 return -1;
432                         }
433                         wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
434                                    "AT_NONCE_S");
435                         if (alen != 2 + EAP_SIM_NONCE_S_LEN) {
436                                 wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
437                                            "AT_NONCE_S (alen=%lu)",
438                                            (unsigned long) alen);
439                                 return -1;
440                         }
441                         attr->nonce_s = apos + 2;
442                         break;
443                 case EAP_SIM_AT_CLIENT_ERROR_CODE:
444                         if (alen != 2) {
445                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
446                                            "AT_CLIENT_ERROR_CODE length %lu",
447                                            (unsigned long) alen);
448                                 return -1;
449                         }
450                         attr->client_error_code = apos[0] * 256 + apos[1];
451                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_CLIENT_ERROR_CODE "
452                                    "%d", attr->client_error_code);
453                         break;
454                 case EAP_SIM_AT_IV:
455                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IV");
456                         if (alen != 2 + EAP_SIM_MAC_LEN) {
457                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_IV "
458                                            "length %lu", (unsigned long) alen);
459                                 return -1;
460                         }
461                         attr->iv = apos + 2;
462                         break;
463                 case EAP_SIM_AT_ENCR_DATA:
464                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ENCR_DATA");
465                         attr->encr_data = apos + 2;
466                         attr->encr_data_len = alen - 2;
467                         if (attr->encr_data_len % 16) {
468                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
469                                            "AT_ENCR_DATA length %lu",
470                                            (unsigned long)
471                                            attr->encr_data_len);
472                                 return -1;
473                         }
474                         break;
475                 case EAP_SIM_AT_NEXT_PSEUDONYM:
476                         if (!encr) {
477                                 wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
478                                            "AT_NEXT_PSEUDONYM");
479                                 return -1;
480                         }
481                         wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
482                                    "AT_NEXT_PSEUDONYM");
483                         plen = apos[0] * 256 + apos[1];
484                         if (plen > alen - 2) {
485                                 wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
486                                            " AT_NEXT_PSEUDONYM (actual"
487                                            " len %lu, attr len %lu)",
488                                            (unsigned long) plen,
489                                            (unsigned long) alen);
490                                 return -1;
491                         }
492                         attr->next_pseudonym = pos + 4;
493                         attr->next_pseudonym_len = plen;
494                         break;
495                 case EAP_SIM_AT_NEXT_REAUTH_ID:
496                         if (!encr) {
497                                 wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
498                                            "AT_NEXT_REAUTH_ID");
499                                 return -1;
500                         }
501                         wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
502                                    "AT_NEXT_REAUTH_ID");
503                         plen = apos[0] * 256 + apos[1];
504                         if (plen > alen - 2) {
505                                 wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
506                                            " AT_NEXT_REAUTH_ID (actual"
507                                            " len %lu, attr len %lu)",
508                                            (unsigned long) plen,
509                                            (unsigned long) alen);
510                                 return -1;
511                         }
512                         attr->next_reauth_id = pos + 4;
513                         attr->next_reauth_id_len = plen;
514                         break;
515                 case EAP_SIM_AT_RES:
516                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RES");
517                         apos += 2;
518                         alen -= 2;
519                         if (!aka || alen < EAP_AKA_MIN_RES_LEN ||
520                             alen > EAP_AKA_MAX_RES_LEN) {
521                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RES "
522                                            "(len %lu)",
523                                            (unsigned long) alen);
524                                 return -1;
525                         }
526                         attr->res = apos;
527                         attr->res_len = alen;
528                         break;
529                 case EAP_SIM_AT_AUTS:
530                         wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTS");
531                         if (!aka) {
532                                 wpa_printf(MSG_DEBUG, "EAP-SIM: "
533                                            "Unexpected AT_AUTS");
534                                 return -1;
535                         }
536                         if (alen != EAP_AKA_AUTS_LEN) {
537                                 wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTS"
538                                            " (len %lu)",
539                                            (unsigned long) alen);
540                                 return -1;
541                         }
542                         attr->auts = apos;
543                         break;
544                 default:
545                         if (pos[0] < 128) {
546                                 wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized "
547                                            "non-skippable attribute %d",
548                                            pos[0]);
549                                 return -1;
550                         }
551
552                         wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized skippable"
553                                    " attribute %d ignored", pos[0]);
554                         break;
555                 }
556
557                 pos += pos[1] * 4;
558         }
559
560         wpa_printf(MSG_DEBUG, "EAP-SIM: Attributes parsed successfully "
561                    "(aka=%d encr=%d)", aka, encr);
562
563         return 0;
564 }
565
566
567 u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data,
568                         size_t encr_data_len, const u8 *iv,
569                         struct eap_sim_attrs *attr, int aka)
570 {
571         u8 *decrypted;
572
573         if (!iv) {
574                 wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV");
575                 return NULL;
576         }
577
578         decrypted = os_malloc(encr_data_len);
579         if (decrypted == NULL)
580                 return NULL;
581         os_memcpy(decrypted, encr_data, encr_data_len);
582
583         aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len);
584         wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA",
585                     decrypted, encr_data_len);
586
587         if (eap_sim_parse_attr(decrypted, decrypted + encr_data_len, attr,
588                                aka, 1)) {
589                 wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse "
590                            "decrypted AT_ENCR_DATA");
591                 os_free(decrypted);
592                 return NULL;
593         }
594
595         return decrypted;
596 }
597
598
599 #define EAP_SIM_INIT_LEN 128
600
601 struct eap_sim_msg {
602         u8 *buf;
603         size_t buf_len, used;
604         size_t mac, iv, encr; /* index from buf */
605 };
606
607
608 struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype)
609 {
610         struct eap_sim_msg *msg;
611         struct eap_hdr *eap;
612         u8 *pos;
613
614         msg = os_zalloc(sizeof(*msg));
615         if (msg == NULL)
616                 return NULL;
617
618         msg->buf = os_zalloc(EAP_SIM_INIT_LEN);
619         if (msg->buf == NULL) {
620                 os_free(msg);
621                 return NULL;
622         }
623         msg->buf_len = EAP_SIM_INIT_LEN;
624         eap = (struct eap_hdr *) msg->buf;
625         eap->code = code;
626         eap->identifier = id;
627         msg->used = sizeof(*eap);
628
629         pos = (u8 *) (eap + 1);
630         *pos++ = type;
631         *pos++ = subtype;
632         *pos++ = 0; /* Reserved */
633         *pos++ = 0; /* Reserved */
634         msg->used += 4;
635
636         return msg;
637 }
638
639
640 u8 * eap_sim_msg_finish(struct eap_sim_msg *msg, size_t *len, const u8 *k_aut,
641                         const u8 *extra, size_t extra_len)
642 {
643         struct eap_hdr *eap;
644         u8 *buf;
645
646         if (msg == NULL)
647                 return NULL;
648
649         eap = (struct eap_hdr *) msg->buf;
650         eap->length = host_to_be16(msg->used);
651
652         if (k_aut && msg->mac) {
653                 eap_sim_add_mac(k_aut, msg->buf, msg->used,
654                                 msg->buf + msg->mac, extra, extra_len);
655         }
656
657         *len = msg->used;
658         buf = msg->buf;
659         os_free(msg);
660         return buf;
661 }
662
663
664 void eap_sim_msg_free(struct eap_sim_msg *msg)
665 {
666         if (msg) {
667                 os_free(msg->buf);
668                 os_free(msg);
669         }
670 }
671
672
673 static int eap_sim_msg_resize(struct eap_sim_msg *msg, size_t add_len)
674 {
675         if (msg->used + add_len > msg->buf_len) {
676                 u8 *nbuf = os_realloc(msg->buf, msg->used + add_len);
677                 if (nbuf == NULL)
678                         return -1;
679                 msg->buf = nbuf;
680                 msg->buf_len = msg->used + add_len;
681         }
682         return 0;
683 }
684
685
686 u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
687                           const u8 *data, size_t len)
688 {
689         int attr_len = 2 + len;
690         int pad_len;
691         u8 *start, *pos;
692
693         if (msg == NULL)
694                 return NULL;
695
696         pad_len = (4 - attr_len % 4) % 4;
697         attr_len += pad_len;
698         if (eap_sim_msg_resize(msg, attr_len))
699                 return NULL;
700         start = pos = msg->buf + msg->used;
701         *pos++ = attr;
702         *pos++ = attr_len / 4;
703         os_memcpy(pos, data, len);
704         if (pad_len) {
705                 pos += len;
706                 os_memset(pos, 0, pad_len);
707         }
708         msg->used += attr_len;
709         return start;
710 }
711
712
713 u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value,
714                      const u8 *data, size_t len)
715 {
716         int attr_len = 4 + len;
717         int pad_len;
718         u8 *start, *pos;
719
720         if (msg == NULL)
721                 return NULL;
722
723         pad_len = (4 - attr_len % 4) % 4;
724         attr_len += pad_len;
725         if (eap_sim_msg_resize(msg, attr_len))
726                 return NULL;
727         start = pos = msg->buf + msg->used;
728         *pos++ = attr;
729         *pos++ = attr_len / 4;
730         WPA_PUT_BE16(pos, value);
731         pos += 2;
732         if (data)
733                 os_memcpy(pos, data, len);
734         if (pad_len) {
735                 pos += len;
736                 os_memset(pos, 0, pad_len);
737         }
738         msg->used += attr_len;
739         return start;
740 }
741
742
743 u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr)
744 {
745         u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN);
746         if (pos)
747                 msg->mac = (pos - msg->buf) + 4;
748         return pos;
749 }
750
751
752 int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
753                                u8 attr_encr)
754 {
755         u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN);
756         if (pos == NULL)
757                 return -1;
758         msg->iv = (pos - msg->buf) + 4;
759         if (hostapd_get_rand(msg->buf + msg->iv, EAP_SIM_IV_LEN)) {
760                 msg->iv = 0;
761                 return -1;
762         }
763
764         pos = eap_sim_msg_add(msg, attr_encr, 0, NULL, 0);
765         if (pos == NULL) {
766                 msg->iv = 0;
767                 return -1;
768         }
769         msg->encr = pos - msg->buf;
770
771         return 0;
772 }
773
774
775 int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad)
776 {
777         size_t encr_len;
778
779         if (msg == NULL || k_encr == NULL || msg->iv == 0 || msg->encr == 0)
780                 return -1;
781
782         encr_len = msg->used - msg->encr - 4;
783         if (encr_len % 16) {
784                 u8 *pos;
785                 int pad_len = 16 - (encr_len % 16);
786                 if (pad_len < 4) {
787                         wpa_printf(MSG_WARNING, "EAP-SIM: "
788                                    "eap_sim_msg_add_encr_end - invalid pad_len"
789                                    " %d", pad_len);
790                         return -1;
791                 }
792                 wpa_printf(MSG_DEBUG, "   *AT_PADDING");
793                 pos = eap_sim_msg_add(msg, attr_pad, 0, NULL, pad_len - 4);
794                 if (pos == NULL)
795                         return -1;
796                 os_memset(pos + 4, 0, pad_len - 4);
797                 encr_len += pad_len;
798         }
799         wpa_printf(MSG_DEBUG, "   (AT_ENCR_DATA data len %lu)",
800                    (unsigned long) encr_len);
801         msg->buf[msg->encr + 1] = encr_len / 4 + 1;
802         aes_128_cbc_encrypt(k_encr, msg->buf + msg->iv,
803                             msg->buf + msg->encr + 4, encr_len);
804
805         return 0;
806 }
807
808
809 void eap_sim_report_notification(void *msg_ctx, int notification, int aka)
810 {
811 #ifndef CONFIG_NO_STDOUT_DEBUG
812         const char *type = aka ? "AKA" : "SIM";
813 #endif /* CONFIG_NO_STDOUT_DEBUG */
814
815         switch (notification) {
816         case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH:
817                 wpa_printf(MSG_WARNING, "EAP-%s: General failure "
818                            "notification (after authentication)", type);
819                 break;
820         case EAP_SIM_TEMPORARILY_DENIED:
821                 wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
822                            "User has been temporarily denied access to the "
823                            "requested service", type);
824                 break;
825         case EAP_SIM_NOT_SUBSCRIBED:
826                 wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
827                            "User has not subscribed to the requested service",
828                            type);
829                 break;
830         case EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH:
831                 wpa_printf(MSG_WARNING, "EAP-%s: General failure "
832                            "notification (before authentication)", type);
833                 break;
834         case EAP_SIM_SUCCESS:
835                 wpa_printf(MSG_INFO, "EAP-%s: Successful authentication "
836                            "notification", type);
837                 break;
838         default:
839                 if (notification >= 32768) {
840                         wpa_printf(MSG_INFO, "EAP-%s: Unrecognized "
841                                    "non-failure notification %d",
842                                    type, notification);
843                 } else {
844                         wpa_printf(MSG_WARNING, "EAP-%s: Unrecognized "
845                                    "failure notification %d",
846                                    type, notification);
847                 }
848         }
849 }