sim_provider_t API gained support for pseudonym/fast reauthentication
[strongswan.git] / src / charon / plugins / eap_aka_3gpp2 / eap_aka_3gpp2_provider.c
1 /*
2 * Copyright (C) 2008-2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "eap_aka_3gpp2_provider.h"
17
18 #include <daemon.h>
19 #include <credentials/keys/shared_key.h>
20
21 typedef struct private_eap_aka_3gpp2_provider_t private_eap_aka_3gpp2_provider_t;
22
23 /**
24 * Private data of an eap_aka_3gpp2_provider_t object.
25 */
26 struct private_eap_aka_3gpp2_provider_t {
27
28 /**
29 * Public eap_aka_3gpp2_provider_t interface.
30 */
31 eap_aka_3gpp2_provider_t public;
32
33 /**
34 * AKA functions
35 */
36 eap_aka_3gpp2_functions_t *f;
37
38 /**
39 * time based SQN, we use the same for all peers
40 */
41 char sqn[AKA_SQN_LEN];
42 };
43
44 /** Authentication management field */
45 static char amf[AKA_AMF_LEN] = {0x00, 0x01};
46
47 /**
48 * Get a shared key K from the credential database
49 */
50 bool eap_aka_3gpp2_get_k(identification_t *id, char k[AKA_K_LEN])
51 {
52 shared_key_t *shared;
53 chunk_t key;
54
55 shared = charon->credentials->get_shared(charon->credentials,
56 SHARED_EAP, id, NULL);
57 if (shared == NULL)
58 {
59 return FALSE;
60 }
61 key = shared->get_key(shared);
62 memset(k, '\0', AKA_K_LEN);
63 memcpy(k, key.ptr, min(key.len, AKA_K_LEN));
64 shared->destroy(shared);
65 return TRUE;
66 }
67
68 /**
69 * get SQN using current time
70 */
71 void eap_aka_3gpp2_get_sqn(char sqn[AKA_SQN_LEN], int offset)
72 {
73 timeval_t time;
74
75 gettimeofday(&time, NULL);
76 /* set sqn to an integer containing 4 bytes seconds + 2 bytes usecs */
77 time.tv_sec = htonl(time.tv_sec + offset);
78 /* usec's are never larger than 0x000f423f, so we shift the 12 first bits */
79 time.tv_usec = htonl(time.tv_usec << 12);
80 memcpy(sqn, (char*)&time.tv_sec + sizeof(time_t) - 4, 4);
81 memcpy(sqn + 4, &time.tv_usec, 2);
82 }
83
84 /**
85 * Implementation of usim_provider_t.get_quintuplet
86 */
87 static bool get_quintuplet(private_eap_aka_3gpp2_provider_t *this,
88 identification_t *imsi, char rand[AKA_RAND_LEN],
89 char xres[AKA_RES_LEN], char ck[AKA_CK_LEN],
90 char ik[AKA_IK_LEN], char autn[AKA_AUTN_LEN])
91 {
92 rng_t *rng;
93 char mac[AKA_MAC_LEN], ak[AKA_AK_LEN], k[AKA_K_LEN];
94
95 /* generate RAND: we use a registered RNG, not f0() proposed in S.S0055 */
96 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
97 if (!rng)
98 {
99 DBG1(DBG_IKE, "generating RAND for AKA failed");
100 return FALSE;
101 }
102 rng->get_bytes(rng, AKA_RAND_LEN, rand);
103 rng->destroy(rng);
104
105 if (!eap_aka_3gpp2_get_k(imsi, k))
106 {
107 DBG1(DBG_IKE, "no EAP key found for %Y to authenticate with AKA", imsi);
108 return FALSE;
109 }
110
111 DBG3(DBG_IKE, "generated rand %b", rand, AKA_RAND_LEN);
112 DBG3(DBG_IKE, "using K %b", k, AKA_K_LEN);
113
114 /* MAC */
115 this->f->f1(this->f, k, rand, this->sqn, amf, mac);
116 /* AK */
117 this->f->f5(this->f, k, rand, ak);
118 /* XRES as expected from client */
119 this->f->f2(this->f, k, rand, xres);
120 /* AUTN = (SQN xor AK) || AMF || MAC */
121 memcpy(autn, this->sqn, AKA_SQN_LEN);
122 memxor(autn, ak, AKA_AK_LEN);
123 memcpy(autn + AKA_SQN_LEN, amf, AKA_AMF_LEN);
124 memcpy(autn + AKA_SQN_LEN + AKA_AMF_LEN, mac, AKA_MAC_LEN);
125 DBG3(DBG_IKE, "AUTN %b", autn, AKA_AUTN_LEN);
126 /* CK/IK */
127 this->f->f3(this->f, k, rand, ck);
128 this->f->f4(this->f, k, rand, ik);
129
130 return TRUE;
131 }
132
133 /**
134 * Implementation of usim_provider_t.resync
135 */
136 static bool resync(private_eap_aka_3gpp2_provider_t *this,
137 identification_t *imsi, char rand[AKA_RAND_LEN],
138 char auts[AKA_AUTS_LEN])
139 {
140 char *sqn, *macs;
141 char aks[AKA_AK_LEN], k[AKA_K_LEN], amf[AKA_AMF_LEN], xmacs[AKA_MAC_LEN];
142
143 if (!eap_aka_3gpp2_get_k(imsi, k))
144 {
145 DBG1(DBG_IKE, "no EAP key found for %Y to authenticate with AKA", imsi);
146 return FALSE;
147 }
148
149 /* AUTHS = (AK xor SQN) | MAC */
150 sqn = auts;
151 macs = auts + AKA_SQN_LEN;
152 this->f->f5star(this->f, k, rand, aks);
153 memxor(sqn, aks, AKA_AK_LEN);
154
155 /* verify XMACS, AMF of zero is used in resynchronization */
156 memset(amf, 0, AKA_AMF_LEN);
157 this->f->f1star(this->f, k, rand, sqn, amf, xmacs);
158 if (!memeq(macs, xmacs, AKA_MAC_LEN))
159 {
160 DBG1(DBG_IKE, "received MACS does not match XMACS");
161 DBG3(DBG_IKE, "MACS %b XMACS %b",
162 macs, AKA_MAC_LEN, xmacs, AKA_MAC_LEN);
163 return FALSE;
164 }
165 /* update stored SQN to received SQN + 1 */
166 memcpy(this->sqn, sqn, AKA_SQN_LEN);
167 chunk_increment(chunk_create(this->sqn, AKA_SQN_LEN));
168 return TRUE;
169 }
170
171 /**
172 * Implementation of eap_aka_3gpp2_provider_t.destroy.
173 */
174 static void destroy(private_eap_aka_3gpp2_provider_t *this)
175 {
176 free(this);
177 }
178
179 /**
180 * See header
181 */
182 eap_aka_3gpp2_provider_t *eap_aka_3gpp2_provider_create(
183 eap_aka_3gpp2_functions_t *f)
184 {
185 private_eap_aka_3gpp2_provider_t *this = malloc_thing(private_eap_aka_3gpp2_provider_t);
186
187 this->public.provider.get_triplet = (bool(*)(sim_provider_t*, identification_t *imsi, char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN]))return_false;
188 this->public.provider.get_quintuplet = (bool(*)(sim_provider_t*, identification_t *imsi, char rand[AKA_RAND_LEN], char xres[AKA_RES_LEN], char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], char autn[AKA_AUTN_LEN]))get_quintuplet;
189 this->public.provider.resync = (bool(*)(sim_provider_t*, identification_t *imsi, char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]))resync;
190 this->public.provider.gen_pseudonym = (identification_t*(*)(sim_provider_t*, identification_t *id))return_null;
191 this->public.provider.is_reauth = (bool(*)(sim_provider_t*, identification_t *id, char [HASH_SIZE_SHA1], u_int16_t *counter))return_false;
192 this->public.provider.gen_reauth = (identification_t*(*)(sim_provider_t*, identification_t *id, char mk[HASH_SIZE_SHA1]))return_null;
193 this->public.destroy = (void(*)(eap_aka_3gpp2_provider_t*))destroy;
194
195 this->f = f;
196 /* use an offset to accept clock skew between client/server without resync */
197 eap_aka_3gpp2_get_sqn(this->sqn, 180);
198
199 return &this->public;
200 }
201