]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/wpa_supplicant/eap_sim_common.c
This commit was generated by cvs2svn to compensate for changes in r156066,
[FreeBSD/FreeBSD.git] / contrib / wpa_supplicant / eap_sim_common.c
1 /*
2  * WPA Supplicant / EAP-SIM/AKA shared routines
3  * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18
19 #include "common.h"
20 #include "eap_i.h"
21 #include "sha1.h"
22 #include "aes_wrap.h"
23 #include "eap_sim_common.h"
24
25
26 #define MSK_LEN 8
27 #define EMSK_LEN 8
28
29
30 static void eap_sim_prf(const u8 *key, u8 *x, size_t xlen)
31 {
32         u8 xkey[64];
33         u32 t[5], _t[5];
34         int i, j, m, k;
35         u8 *xpos = x;
36         u32 carry;
37
38         /* FIPS 186-2 + change notice 1 */
39
40         memcpy(xkey, key, EAP_SIM_MK_LEN);
41         memset(xkey + EAP_SIM_MK_LEN, 0, 64 - EAP_SIM_MK_LEN);
42         t[0] = 0x67452301;
43         t[1] = 0xEFCDAB89;
44         t[2] = 0x98BADCFE;
45         t[3] = 0x10325476;
46         t[4] = 0xC3D2E1F0;
47
48         m = xlen / 40;
49         for (j = 0; j < m; j++) {
50                 /* XSEED_j = 0 */
51                 for (i = 0; i < 2; i++) {
52                         /* XVAL = (XKEY + XSEED_j) mod 2^b */
53
54                         /* w_i = G(t, XVAL) */
55                         memcpy(_t, t, 20);
56                         sha1_transform((u8 *) _t, xkey);
57                         _t[0] = host_to_be32(_t[0]);
58                         _t[1] = host_to_be32(_t[1]);
59                         _t[2] = host_to_be32(_t[2]);
60                         _t[3] = host_to_be32(_t[3]);
61                         _t[4] = host_to_be32(_t[4]);
62                         memcpy(xpos, _t, 20);
63
64                         /* XKEY = (1 + XKEY + w_i) mod 2^b */
65                         carry = 1;
66                         for (k = 19; k >= 0; k--) {
67                                 carry += xkey[k] + xpos[k];
68                                 xkey[k] = carry & 0xff;
69                                 carry >>= 8;
70                         }
71
72                         xpos += SHA1_MAC_LEN;
73                 }
74                 /* x_j = w_0|w_1 */
75         }
76 }
77
78
79 void eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk)
80 {
81         u8 buf[120], *pos;
82         eap_sim_prf(mk, buf, 120);
83         pos = buf;
84         memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN);
85         pos += EAP_SIM_K_ENCR_LEN;
86         memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN);
87         pos += EAP_SIM_K_AUT_LEN;
88         memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN);
89         pos += MSK_LEN;
90
91         wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr",
92                         k_encr, EAP_SIM_K_ENCR_LEN);
93         wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut",
94                         k_aut, EAP_SIM_K_ENCR_LEN);
95         wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MSK",
96                         msk, MSK_LEN);
97         wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Ext. MSK",
98                         msk + MSK_LEN, EMSK_LEN);
99         wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material",
100                     msk, EAP_SIM_KEYING_DATA_LEN);
101 }
102
103
104 void eap_sim_derive_keys_reauth(unsigned int _counter,
105                                 const u8 *identity, size_t identity_len,
106                                 const u8 *nonce_s, const u8 *mk, u8 *msk)
107 {
108         u8 xkey[SHA1_MAC_LEN];
109         u8 counter[2];
110         const u8 *addr[4];
111         size_t len[4];
112
113         addr[0] = identity;
114         len[0] = identity_len;
115         addr[1] = counter;
116         len[1] = 2;
117         addr[2] = nonce_s;
118         len[2] = EAP_SIM_NONCE_S_LEN;
119         addr[3] = mk;
120         len[3] = EAP_SIM_MK_LEN;
121
122         counter[0] = _counter >> 8;
123         counter[1] = _counter & 0xff;
124
125         wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth");
126         wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
127                           identity, identity_len);
128         wpa_hexdump(MSG_DEBUG, "EAP-SIM: counter", counter, 2);
129         wpa_hexdump(MSG_DEBUG, "EAP-SIM: NONCE_S", nonce_s,
130                     EAP_SIM_NONCE_S_LEN);
131         wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN);
132
133         /* XKEY' = SHA1(Identity|counter|NONCE_S|MK) */
134         sha1_vector(4, addr, len, xkey);
135         wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN);
136
137         eap_sim_prf(xkey, msk, EAP_SIM_KEYING_DATA_LEN);
138         wpa_hexdump(MSG_DEBUG, "EAP-SIM: MSK", msk, MSK_LEN);
139         wpa_hexdump(MSG_DEBUG, "EAP-SIM: Ext. MSK", msk + MSK_LEN, EMSK_LEN);
140         wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material",
141                     msk, EAP_SIM_KEYING_DATA_LEN);
142 }
143
144
145 int eap_sim_verify_mac(const u8 *k_aut, u8 *req, size_t req_len, u8 *mac,
146                        u8 *extra, size_t extra_len)
147 {
148         unsigned char hmac[SHA1_MAC_LEN];
149         const u8 *addr[2];
150         size_t len[2];
151         u8 rx_mac[EAP_SIM_MAC_LEN];
152
153         if (mac == NULL)
154                 return -1;
155
156         addr[0] = req;
157         len[0] = req_len;
158         addr[1] = extra;
159         len[1] = extra_len;
160
161         /* HMAC-SHA1-128 */
162         memcpy(rx_mac, mac, EAP_SIM_MAC_LEN);
163         memset(mac, 0, EAP_SIM_MAC_LEN);
164         hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
165         memcpy(mac, rx_mac, EAP_SIM_MAC_LEN);
166
167         return (memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
168 }
169
170
171 void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac,
172                      const u8 *extra, size_t extra_len)
173 {
174         unsigned char hmac[SHA1_MAC_LEN];
175         const u8 *addr[2];
176         size_t len[2];
177
178         addr[0] = msg;
179         len[0] = msg_len;
180         addr[1] = extra;
181         len[1] = extra_len;
182
183         /* HMAC-SHA1-128 */
184         memset(mac, 0, EAP_SIM_MAC_LEN);
185         hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
186         memcpy(mac, hmac, EAP_SIM_MAC_LEN);
187 }
188
189
190 int eap_sim_parse_attr(u8 *start, u8 *end, struct eap_sim_attrs *attr, int aka,
191                        int encr)
192 {
193         u8 *pos = start, *apos;
194         size_t alen, plen;
195         int list_len, i;
196
197         memset(attr, 0, sizeof(*attr));
198         attr->id_req = NO_ID_REQ;
199         attr->notification = -1;
200         attr->counter = -1;
201         attr->selected_version = -1;
202         attr->client_error_code = -1;
203
204         while (pos < end) {
205                 if (pos + 2 > end) {
206                         wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow(1)");
207                         return -1;
208                 }
209                 wpa_printf(MSG_MSGDUMP, "EAP-SIM: Attribute: Type=%d Len=%d",
210                            pos[0], pos[1] * 4);
211                 if (pos + pos[1] * 4 > end) {
212                         wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow "
213                                    "(pos=%p len=%d end=%p)",
214                                    pos, pos[1] * 4, end);
215                         return -1;
216                 }
217                 apos = pos + 2;
218                 alen = pos[1] * 4 - 2;
219                 wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Attribute data",
220                             apos, alen);
221
222                 switch (pos[0]) {
223                 case EAP_SIM_AT_RAND:
224                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RAND");
225                         apos += 2;
226                         alen -= 2;
227                         if ((!aka && (alen % GSM_RAND_LEN)) ||
228                             (aka && alen != AKA_RAND_LEN)) {
229                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND"
230                                            " (len %lu)",
231                                            (unsigned long) alen);
232                                 return -1;
233                         }
234                         attr->rand = apos;
235                         attr->num_chal = alen / GSM_RAND_LEN;
236                         break;
237                 case EAP_SIM_AT_AUTN:
238                         wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTN");
239                         if (!aka) {
240                                 wpa_printf(MSG_DEBUG, "EAP-SIM: "
241                                            "Unexpected AT_AUTN");
242                                 return -1;
243                         }
244                         apos += 2;
245                         alen -= 2;
246                         if (alen != AKA_AUTN_LEN) {
247                                 wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN"
248                                            " (len %lu)",
249                                            (unsigned long) alen);
250                                 return -1;
251                         }
252                         attr->autn = apos;
253                         break;
254                 case EAP_SIM_AT_PADDING:
255                         if (!encr) {
256                                 wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
257                                            "AT_PADDING");
258                                 return -1;
259                         }
260                         wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_PADDING");
261                         for (i = 2; i < alen; i++) {
262                                 if (apos[i] != 0) {
263                                         wpa_printf(MSG_INFO, "EAP-SIM: (encr) "
264                                                    "AT_PADDING used a non-zero"
265                                                    " padding byte");
266                                         wpa_hexdump(MSG_DEBUG, "EAP-SIM: "
267                                                     "(encr) padding bytes",
268                                                     apos + 2, alen - 2);
269                                         return -1;
270                                 }
271                         }
272                         break;
273                 case EAP_SIM_AT_NONCE_MT:
274                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NONCE_MT");
275                         if (alen != 2 + EAP_SIM_NONCE_MT_LEN) {
276                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
277                                            "AT_NONCE_MT length");
278                                 return -1;
279                         }
280                         attr->nonce_mt = apos + 2;
281                         break;
282                 case EAP_SIM_AT_PERMANENT_ID_REQ:
283                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_PERMANENT_ID_REQ");
284                         attr->id_req = PERMANENT_ID;
285                         break;
286                 case EAP_SIM_AT_MAC:
287                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_MAC");
288                         if (alen != 2 + EAP_SIM_MAC_LEN) {
289                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_MAC "
290                                            "length");
291                                 return -1;
292                         }
293                         attr->mac = apos + 2;
294                         break;
295                 case EAP_SIM_AT_NOTIFICATION:
296                         if (alen != 2) {
297                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
298                                            "AT_NOTIFICATION length %lu",
299                                            (unsigned long) alen);
300                                 return -1;
301                         }
302                         attr->notification = apos[0] * 256 + apos[1];
303                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NOTIFICATION %d",
304                                    attr->notification);
305                         break;
306                 case EAP_SIM_AT_ANY_ID_REQ:
307                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ANY_ID_REQ");
308                         attr->id_req = ANY_ID;
309                         break;
310                 case EAP_SIM_AT_IDENTITY:
311                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY");
312                         attr->identity = apos + 2;
313                         attr->identity_len = alen - 2;
314                         break;
315                 case EAP_SIM_AT_VERSION_LIST:
316                         if (aka) {
317                                 wpa_printf(MSG_DEBUG, "EAP-AKA: "
318                                            "Unexpected AT_VERSION_LIST");
319                                 return -1;
320                         }
321                         list_len = apos[0] * 256 + apos[1];
322                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST");
323                         if (list_len < 2 || list_len > alen - 2) {
324                                 wpa_printf(MSG_WARNING, "EAP-SIM: Invalid "
325                                            "AT_VERSION_LIST (list_len=%d "
326                                            "attr_len=%lu)", list_len,
327                                            (unsigned long) alen);
328                                 return -1;
329                         }
330                         attr->version_list = apos + 2;
331                         attr->version_list_len = list_len;
332                         break;
333                 case EAP_SIM_AT_SELECTED_VERSION:
334                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION");
335                         if (alen != 2) {
336                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
337                                            "AT_SELECTED_VERSION length %lu",
338                                            (unsigned long) alen);
339                                 return -1;
340                         }
341                         attr->selected_version = apos[0] * 256 + apos[1];
342                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION "
343                                    "%d", attr->selected_version);
344                         break;
345                 case EAP_SIM_AT_FULLAUTH_ID_REQ:
346                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_FULLAUTH_ID_REQ");
347                         attr->id_req = FULLAUTH_ID;
348                         break;
349                 case EAP_SIM_AT_COUNTER:
350                         if (!encr) {
351                                 wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
352                                            "AT_COUNTER");
353                                 return -1;
354                         }
355                         if (alen != 2) {
356                                 wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
357                                            "AT_COUNTER (alen=%lu)",
358                                            (unsigned long) alen);
359                                 return -1;
360                         }
361                         attr->counter = apos[0] * 256 + apos[1];
362                         wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d",
363                                    attr->counter);
364                         break;
365                 case EAP_SIM_AT_NONCE_S:
366                         if (!encr) {
367                                 wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
368                                            "AT_NONCE_S");
369                                 return -1;
370                         }
371                         wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
372                                    "AT_NONCE_S");
373                         if (alen != 2 + EAP_SIM_NONCE_S_LEN) {
374                                 wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
375                                            "AT_NONCE_S (alen=%lu)",
376                                            (unsigned long) alen);
377                                 return -1;
378                         }
379                         attr->nonce_s = apos + 2;
380                         break;
381                 case EAP_SIM_AT_CLIENT_ERROR_CODE:
382                         if (alen != 2) {
383                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
384                                            "AT_CLIENT_ERROR_CODE length %lu",
385                                            (unsigned long) alen);
386                                 return -1;
387                         }
388                         attr->client_error_code = apos[0] * 256 + apos[1];
389                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_CLIENT_ERROR_CODE "
390                                    "%d", attr->client_error_code);
391                         break;
392                 case EAP_SIM_AT_IV:
393                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IV");
394                         if (alen != 2 + EAP_SIM_MAC_LEN) {
395                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_IV "
396                                            "length %lu", (unsigned long) alen);
397                                 return -1;
398                         }
399                         attr->iv = apos + 2;
400                         break;
401                 case EAP_SIM_AT_ENCR_DATA:
402                         wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ENCR_DATA");
403                         attr->encr_data = apos + 2;
404                         attr->encr_data_len = alen - 2;
405                         if (attr->encr_data_len % 16) {
406                                 wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
407                                            "AT_ENCR_DATA length %lu",
408                                            (unsigned long)
409                                            attr->encr_data_len);
410                                 return -1;
411                         }
412                         break;
413                 case EAP_SIM_AT_NEXT_PSEUDONYM:
414                         if (!encr) {
415                                 wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
416                                            "AT_NEXT_PSEUDONYM");
417                                 return -1;
418                         }
419                         wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
420                                    "AT_NEXT_PSEUDONYM");
421                         plen = apos[0] * 256 + apos[1];
422                         if (plen > alen - 2) {
423                                 wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
424                                            " AT_NEXT_PSEUDONYM (actual"
425                                            " len %lu, attr len %lu)",
426                                            (unsigned long) plen,
427                                            (unsigned long) alen);
428                                 return -1;
429                         }
430                         attr->next_pseudonym = pos + 4;
431                         attr->next_pseudonym_len = plen;
432                         break;
433                 case EAP_SIM_AT_NEXT_REAUTH_ID:
434                         if (!encr) {
435                                 wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
436                                            "AT_NEXT_REAUTH_ID");
437                                 return -1;
438                         }
439                         wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
440                                    "AT_NEXT_REAUTH_ID");
441                         plen = apos[0] * 256 + apos[1];
442                         if (plen > alen - 2) {
443                                 wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
444                                            " AT_NEXT_REAUTH_ID (actual"
445                                            " len %lu, attr len %lu)",
446                                            (unsigned long) plen,
447                                            (unsigned long) alen);
448                                 return -1;
449                         }
450                         attr->next_reauth_id = pos + 4;
451                         attr->next_reauth_id_len = plen;
452                         break;
453                 default:
454                         if (pos[0] < 128) {
455                                 wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized "
456                                            "non-skippable attribute %d",
457                                            pos[0]);
458                                 return -1;
459                         }
460
461                         wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized skippable"
462                                    " attribute %d ignored", pos[0]);
463                         break;
464                 }
465
466                 pos += pos[1] * 4;
467         }
468
469         wpa_printf(MSG_DEBUG, "EAP-SIM: Attributes parsed successfully "
470                    "(aka=%d encr=%d)", aka, encr);
471
472         return 0;
473 }
474
475
476 int eap_sim_parse_encr(const u8 *k_encr, u8 *encr_data, size_t encr_data_len,
477                        const u8 *iv, struct eap_sim_attrs *attr, int aka)
478 {
479         if (!iv) {
480                 wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV");
481                 return -1;
482         }
483         aes_128_cbc_decrypt(k_encr, iv, encr_data, encr_data_len);
484         wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA",
485                     encr_data, encr_data_len);
486
487         if (eap_sim_parse_attr(encr_data, encr_data + encr_data_len, attr,
488                                aka, 1)) {
489                 wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse "
490                            "decrypted AT_ENCR_DATA");
491                 return -1;
492         }
493
494         return 0;
495 }
496
497
498 #define EAP_SIM_INIT_LEN 128
499
500 struct eap_sim_msg {
501         u8 *buf;
502         size_t buf_len, used;
503         size_t mac, iv, encr; /* index from buf */
504 };
505
506
507 struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype)
508 {
509         struct eap_sim_msg *msg;
510         struct eap_hdr *eap;
511         u8 *pos;
512
513         msg = malloc(sizeof(*msg));
514         if (msg == NULL)
515                 return NULL;
516         memset(msg, 0, sizeof(*msg));
517
518         msg->buf = malloc(EAP_SIM_INIT_LEN);
519         if (msg->buf == NULL) {
520                 free(msg);
521                 return NULL;
522         }
523         memset(msg->buf, 0, EAP_SIM_INIT_LEN);
524         msg->buf_len = EAP_SIM_INIT_LEN;
525         eap = (struct eap_hdr *) msg->buf;
526         eap->code = code;
527         eap->identifier = id;
528         msg->used = sizeof(*eap);
529
530         pos = (u8 *) (eap + 1);
531         *pos++ = type;
532         *pos++ = subtype;
533         *pos++ = 0; /* Reserved */
534         *pos++ = 0; /* Reserved */
535         msg->used += 4;
536
537         return msg;
538 }
539
540
541 u8 * eap_sim_msg_finish(struct eap_sim_msg *msg, size_t *len, const u8 *k_aut,
542                         const u8 *extra, size_t extra_len)
543 {
544         struct eap_hdr *eap;
545         u8 *buf;
546
547         if (msg == NULL)
548                 return NULL;
549
550         eap = (struct eap_hdr *) msg->buf;
551         eap->length = host_to_be16(msg->used);
552
553         if (k_aut && msg->mac) {
554                 eap_sim_add_mac(k_aut, msg->buf, msg->used,
555                                 msg->buf + msg->mac, extra, extra_len);
556         }
557
558         *len = msg->used;
559         buf = msg->buf;
560         free(msg);
561         return buf;
562 }
563
564
565 void eap_sim_msg_free(struct eap_sim_msg *msg)
566 {
567         if (msg) {
568                 free(msg->buf);
569                 free(msg);
570         }
571 }
572
573
574 static int eap_sim_msg_resize(struct eap_sim_msg *msg, size_t add_len)
575 {
576         if (msg->used + add_len > msg->buf_len) {
577                 u8 *nbuf = realloc(msg->buf, msg->used + add_len);
578                 if (nbuf == NULL)
579                         return -1;
580                 msg->buf = nbuf;
581                 msg->buf_len = msg->used + add_len;
582         }
583         return 0;
584 }
585
586
587 u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
588                           const u8 *data, size_t len)
589 {
590         int attr_len = 2 + len;
591         int pad_len;
592         u8 *start, *pos;
593
594         if (msg == NULL)
595                 return NULL;
596
597         pad_len = (4 - attr_len % 4) % 4;
598         attr_len += pad_len;
599         if (eap_sim_msg_resize(msg, attr_len))
600                 return NULL;
601         start = pos = msg->buf + msg->used;
602         *pos++ = attr;
603         *pos++ = attr_len / 4;
604         memcpy(pos, data, len);
605         if (pad_len) {
606                 pos += len;
607                 memset(pos, 0, pad_len);
608         }
609         msg->used += attr_len;
610         return start;
611 }
612
613
614 u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value,
615                      const u8 *data, size_t len)
616 {
617         int attr_len = 4 + len;
618         int pad_len;
619         u8 *start, *pos;
620
621         if (msg == NULL)
622                 return NULL;
623
624         pad_len = (4 - attr_len % 4) % 4;
625         attr_len += pad_len;
626         if (eap_sim_msg_resize(msg, attr_len))
627                 return NULL;
628         start = pos = msg->buf + msg->used;
629         *pos++ = attr;
630         *pos++ = attr_len / 4;
631         *pos++ = value >> 8;
632         *pos++ = value & 0xff;
633         if (data)
634                 memcpy(pos, data, len);
635         if (pad_len) {
636                 pos += len;
637                 memset(pos, 0, pad_len);
638         }
639         msg->used += attr_len;
640         return start;
641 }
642
643
644 u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr)
645 {
646         u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN);
647         if (pos)
648                 msg->mac = (pos - msg->buf) + 4;
649         return pos;
650 }
651
652
653 int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
654                                u8 attr_encr)
655 {
656         u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN);
657         if (pos == NULL)
658                 return -1;
659         msg->iv = (pos - msg->buf) + 4;
660         if (hostapd_get_rand(msg->buf + msg->iv, EAP_SIM_IV_LEN)) {
661                 msg->iv = 0;
662                 return -1;
663         }
664
665         pos = eap_sim_msg_add(msg, attr_encr, 0, NULL, 0);
666         if (pos == NULL) {
667                 msg->iv = 0;
668                 return -1;
669         }
670         msg->encr = pos - msg->buf;
671
672         return 0;
673 }
674
675
676 int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad)
677 {
678         size_t encr_len;
679
680         if (k_encr == NULL || msg->iv == 0 || msg->encr == 0)
681                 return -1;
682
683         encr_len = msg->used - msg->encr - 4;
684         if (encr_len % 16) {
685                 u8 *pos;
686                 int pad_len = 16 - (encr_len % 16);
687                 if (pad_len < 4) {
688                         wpa_printf(MSG_WARNING, "EAP-SIM: "
689                                    "eap_sim_msg_add_encr_end - invalid pad_len"
690                                    " %d", pad_len);
691                         return -1;
692                 }
693                 wpa_printf(MSG_DEBUG, "   *AT_PADDING");
694                 pos = eap_sim_msg_add(msg, attr_pad, 0, NULL, pad_len - 4);
695                 if (pos == NULL)
696                         return -1;
697                 memset(pos + 4, 0, pad_len - 4);
698                 encr_len += pad_len;
699         }
700         wpa_printf(MSG_DEBUG, "   (AT_ENCR_DATA data len %lu)",
701                    (unsigned long) encr_len);
702         msg->buf[msg->encr + 1] = encr_len / 4 + 1;
703         aes_128_cbc_encrypt(k_encr, msg->buf + msg->iv,
704                             msg->buf + msg->encr + 4, encr_len);
705
706         return 0;
707 }
708
709
710 void eap_sim_report_notification(void *msg_ctx, int notification, int aka)
711 {
712         const char *type = aka ? "AKA" : "SIM";
713
714         switch (notification) {
715         case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH:
716                 wpa_printf(MSG_WARNING, "EAP-%s: General failure "
717                            "notification (after authentication)", type);
718                 break;
719         case EAP_SIM_TEMPORARILY_DENIED:
720                 wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
721                            "User has been temporarily denied access to the "
722                            "requested service", type);
723                 break;
724         case EAP_SIM_NOT_SUBSCRIBED:
725                 wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
726                            "User has not subscribed to the requested service",
727                            type);
728                 break;
729         case EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH:
730                 wpa_printf(MSG_WARNING, "EAP-%s: General failure "
731                            "notification (before authentication)", type);
732                 break;
733         case EAP_SIM_SUCCESS:
734                 wpa_printf(MSG_INFO, "EAP-%s: Successful authentication "
735                            "notification", type);
736                 break;
737         default:
738                 if (notification >= 32768) {
739                         wpa_printf(MSG_INFO, "EAP-%s: Unrecognized "
740                                    "non-failure notification %d",
741                                    type, notification);
742                 } else {
743                         wpa_printf(MSG_WARNING, "EAP-%s: Unrecognized "
744                                    "failure notification %d",
745                                    type, notification);
746                 }
747         }
748 }
749
750
751 #ifdef TEST_MAIN_EAP_SIM_COMMON
752 static int test_eap_sim_prf(void)
753 {
754         /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */
755         u8 xkey[] = {
756                 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b,
757                 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f,
758                 0xeb, 0x5a, 0x38, 0xb6
759         };
760         u8 w[] = {
761                 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f,
762                 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49,
763                 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba,
764                 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78,
765                 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16
766         };
767         u8 buf[40];
768
769         printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n");
770         eap_sim_prf(xkey, buf, sizeof(buf));
771         if (memcmp(w, buf, sizeof(w) != 0)) {
772                 printf("eap_sim_prf failed\n");
773                 return 1;
774         }
775
776         return 0;
777 }
778
779
780 int main(int argc, char *argv[])
781 {
782         int errors = 0;
783
784         errors += test_eap_sim_prf();
785
786         return errors;
787 }
788 #endif /* TEST_MAIN_EAP_SIM_COMMON */