eap-aka-3gpp2: Increase SQN after each authentication
[strongswan.git] / src / libcharon / plugins / eap_aka_3gpp2 / eap_aka_3gpp2_provider.c
1 /*
2 * Copyright (C) 2008-2009 Martin Willi
3 * HSR 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 = lib->credmgr->get_shared(lib->credmgr, SHARED_EAP, id, NULL);
56 if (shared == NULL)
57 {
58 return FALSE;
59 }
60 key = shared->get_key(shared);
61 memset(k, '\0', AKA_K_LEN);
62 memcpy(k, key.ptr, min(key.len, AKA_K_LEN));
63 shared->destroy(shared);
64 return TRUE;
65 }
66
67 /**
68 * get SQN using current time
69 */
70 void eap_aka_3gpp2_get_sqn(char sqn[AKA_SQN_LEN], int offset)
71 {
72 timeval_t time;
73
74 gettimeofday(&time, NULL);
75 /* set sqn to an integer containing 4 bytes seconds + 2 bytes usecs */
76 time.tv_sec = htonl(time.tv_sec + offset);
77 /* usec's are never larger than 0x000f423f, so we shift the 12 first bits */
78 time.tv_usec = htonl(time.tv_usec << 12);
79 memcpy(sqn, (char*)&time.tv_sec + sizeof(time_t) - 4, 4);
80 memcpy(sqn + 4, &time.tv_usec, 2);
81 }
82
83 METHOD(simaka_provider_t, get_quintuplet, bool,
84 private_eap_aka_3gpp2_provider_t *this, identification_t *id,
85 char rand[AKA_RAND_LEN], char xres[AKA_RES_MAX], int *xres_len,
86 char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], char autn[AKA_AUTN_LEN])
87 {
88 rng_t *rng;
89 char mac[AKA_MAC_LEN], ak[AKA_AK_LEN], k[AKA_K_LEN];
90
91 /* generate RAND: we use a registered RNG, not f0() proposed in S.S0055 */
92 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
93 if (!rng || !rng->get_bytes(rng, AKA_RAND_LEN, rand))
94 {
95 DBG1(DBG_IKE, "generating RAND for AKA failed");
96 DESTROY_IF(rng);
97 return FALSE;
98 }
99 rng->destroy(rng);
100
101 if (!eap_aka_3gpp2_get_k(id, k))
102 {
103 DBG1(DBG_IKE, "no EAP key found for %Y to authenticate with AKA", id);
104 return FALSE;
105 }
106
107 DBG3(DBG_IKE, "generated rand %b", rand, AKA_RAND_LEN);
108 DBG3(DBG_IKE, "using K %b", k, AKA_K_LEN);
109
110 /* MAC, AK, XRES as expected from client */
111 if (!this->f->f1(this->f, k, rand, this->sqn, amf, mac) ||
112 !this->f->f5(this->f, k, rand, ak) ||
113 !this->f->f2(this->f, k, rand, xres))
114 {
115 return FALSE;
116 }
117 *xres_len = AKA_RES_MAX;
118 /* AUTN = (SQN xor AK) || AMF || MAC */
119 memcpy(autn, this->sqn, AKA_SQN_LEN);
120 memxor(autn, ak, AKA_AK_LEN);
121 memcpy(autn + AKA_SQN_LEN, amf, AKA_AMF_LEN);
122 memcpy(autn + AKA_SQN_LEN + AKA_AMF_LEN, mac, AKA_MAC_LEN);
123 DBG3(DBG_IKE, "AUTN %b", autn, AKA_AUTN_LEN);
124 /* CK/IK */
125 if (!this->f->f3(this->f, k, rand, ck) ||
126 !this->f->f4(this->f, k, rand, ik))
127 {
128 return FALSE;
129 }
130 chunk_increment(chunk_create(this->sqn, AKA_SQN_LEN));
131 return TRUE;
132 }
133
134 METHOD(simaka_provider_t, resync, bool,
135 private_eap_aka_3gpp2_provider_t *this, identification_t *id,
136 char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN])
137 {
138 char *sqn, *macs;
139 char aks[AKA_AK_LEN], k[AKA_K_LEN], amf[AKA_AMF_LEN], xmacs[AKA_MAC_LEN];
140
141 if (!eap_aka_3gpp2_get_k(id, k))
142 {
143 DBG1(DBG_IKE, "no EAP key found for %Y to authenticate with AKA", id);
144 return FALSE;
145 }
146
147 /* AUTHS = (AK xor SQN) | MAC */
148 sqn = auts;
149 macs = auts + AKA_SQN_LEN;
150 if (!this->f->f5star(this->f, k, rand, aks))
151 {
152 return FALSE;
153 }
154 memxor(sqn, aks, AKA_AK_LEN);
155
156 /* verify XMACS, AMF of zero is used in resynchronization */
157 memset(amf, 0, AKA_AMF_LEN);
158 if (!this->f->f1star(this->f, k, rand, sqn, amf, xmacs))
159 {
160 return FALSE;
161 }
162 if (!memeq_const(macs, xmacs, AKA_MAC_LEN))
163 {
164 DBG1(DBG_IKE, "received MACS does not match XMACS");
165 DBG3(DBG_IKE, "MACS %b XMACS %b",
166 macs, AKA_MAC_LEN, xmacs, AKA_MAC_LEN);
167 return FALSE;
168 }
169 /* update stored SQN to received SQN + 1 */
170 memcpy(this->sqn, sqn, AKA_SQN_LEN);
171 chunk_increment(chunk_create(this->sqn, AKA_SQN_LEN));
172 return TRUE;
173 }
174
175 METHOD(eap_aka_3gpp2_provider_t, destroy, void,
176 private_eap_aka_3gpp2_provider_t *this)
177 {
178 free(this);
179 }
180
181 /**
182 * See header
183 */
184 eap_aka_3gpp2_provider_t *eap_aka_3gpp2_provider_create(
185 eap_aka_3gpp2_functions_t *f)
186 {
187 private_eap_aka_3gpp2_provider_t *this;
188
189 INIT(this,
190 .public = {
191 .provider = {
192 .get_triplet = (void*)return_false,
193 .get_quintuplet = _get_quintuplet,
194 .resync = _resync,
195 .is_pseudonym = (void*)return_null,
196 .gen_pseudonym = (void*)return_null,
197 .is_reauth = (void*)return_null,
198 .gen_reauth = (void*)return_null,
199 },
200 .destroy = _destroy,
201 },
202 .f = f,
203 );
204 /* use an offset to accept clock skew between client/server without resync */
205 eap_aka_3gpp2_get_sqn(this->sqn, 180);
206
207 return &this->public;
208 }