2 * Copyright (C) 2006-2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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>.
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
16 #include "eap_aka_peer.h"
21 #include <simaka_message.h>
22 #include <simaka_crypto.h>
24 typedef struct private_eap_aka_peer_t private_eap_aka_peer_t
;
27 * Private data of an eap_aka_peer_t object.
29 struct private_eap_aka_peer_t
{
32 * Public authenticator_t interface.
34 eap_aka_peer_t
public;
37 * EAP-AKA crypto helper
39 simaka_crypto_t
*crypto
;
44 identification_t
*peer
;
53 * Create a AKA_CLIENT_ERROR: "Unable to process"
55 static eap_payload_t
* create_client_error(private_eap_aka_peer_t
*this,
58 simaka_message_t
*message
;
62 DBG1(DBG_IKE
, "sending client error '%N'",
63 simaka_client_error_names
, AKA_UNABLE_TO_PROCESS
);
65 message
= simaka_message_create(FALSE
, identifier
, EAP_AKA
,
66 AKA_CLIENT_ERROR
, this->crypto
);
67 encoded
= htons(AKA_UNABLE_TO_PROCESS
);
68 message
->add_attribute(message
, AT_CLIENT_ERROR_CODE
,
69 chunk_create((char*)&encoded
, sizeof(encoded
)));
70 out
= message
->generate(message
, chunk_empty
);
71 message
->destroy(message
);
76 * Process an EAP-AKA/Request/Challenge message
78 static status_t
process_challenge(private_eap_aka_peer_t
*this,
79 simaka_message_t
*in
, eap_payload_t
**out
)
81 simaka_message_t
*message
;
82 enumerator_t
*enumerator
;
83 simaka_attribute_t type
;
85 chunk_t data
, rand
= chunk_empty
, autn
= chunk_empty
, mk
;
86 u_char res
[AKA_RES_LEN
], ck
[AKA_CK_LEN
], ik
[AKA_IK_LEN
], auts
[AKA_AUTS_LEN
];
87 status_t status
= NOT_FOUND
;
89 enumerator
= in
->create_attribute_enumerator(in
);
90 while (enumerator
->enumerate(enumerator
, &type
, &data
))
101 if (!simaka_attribute_skippable(type
))
103 *out
= create_client_error(this, in
->get_identifier(in
));
104 enumerator
->destroy(enumerator
);
110 enumerator
->destroy(enumerator
);
112 if (!rand
.len
|| !autn
.len
)
114 DBG1(DBG_IKE
, "received invalid EAP-AKA challenge message");
115 *out
= create_client_error(this, in
->get_identifier(in
));
119 enumerator
= charon
->sim
->create_card_enumerator(charon
->sim
);
120 while (enumerator
->enumerate(enumerator
, &card
))
122 status
= card
->get_quintuplet(card
, this->peer
, rand
.ptr
, autn
.ptr
,
124 if (status
!= FAILED
)
125 { /* try next on error */
129 enumerator
->destroy(enumerator
);
131 if (status
== INVALID_STATE
&&
132 card
->resync(card
, this->peer
, rand
.ptr
, auts
))
134 DBG1(DBG_IKE
, "received SQN invalid, sending %N",
135 simaka_subtype_names
, AKA_SYNCHRONIZATION_FAILURE
);
136 message
= simaka_message_create(FALSE
, in
->get_identifier(in
), EAP_AKA
,
137 AKA_SYNCHRONIZATION_FAILURE
, this->crypto
);
138 message
->add_attribute(message
, AT_AUTS
,
139 chunk_create(auts
, AKA_AUTS_LEN
));
140 *out
= message
->generate(message
, chunk_empty
);
141 message
->destroy(message
);
144 if (status
!= SUCCESS
)
146 DBG1(DBG_IKE
, "no USIM found with quintuplets for '%Y', sending %N",
147 this->peer
, simaka_subtype_names
, AKA_AUTHENTICATION_REJECT
);
148 message
= simaka_message_create(FALSE
, in
->get_identifier(in
), EAP_AKA
,
149 AKA_AUTHENTICATION_REJECT
, this->crypto
);
150 *out
= message
->generate(message
, chunk_empty
);
151 message
->destroy(message
);
155 data
= chunk_cata("cc", chunk_create(ik
, AKA_IK_LEN
),
156 chunk_create(ck
, AKA_CK_LEN
));
158 this->msk
= this->crypto
->derive_keys_full(this->crypto
, this->peer
,
162 /* verify EAP message MAC AT_MAC */
163 if (!in
->verify(in
, chunk_empty
))
165 DBG1(DBG_IKE
, "AT_MAC verification failed ");
166 *out
= create_client_error(this, in
->get_identifier(in
));
170 message
= simaka_message_create(FALSE
, in
->get_identifier(in
), EAP_AKA
,
171 AKA_CHALLENGE
, this->crypto
);
172 message
->add_attribute(message
, AT_RES
, chunk_create(res
, AKA_RES_LEN
));
173 *out
= message
->generate(message
, chunk_empty
);
174 message
->destroy(message
);
179 * Process an EAP-AKA/Request/Identity message
181 static status_t
process_identity(private_eap_aka_peer_t
*this,
182 simaka_message_t
*in
, eap_payload_t
**out
)
184 simaka_message_t
*message
;
185 enumerator_t
*enumerator
;
186 simaka_attribute_t type
;
189 enumerator
= in
->create_attribute_enumerator(in
);
190 while (enumerator
->enumerate(enumerator
, &type
, &data
))
194 case AT_PERMANENT_ID_REQ
:
195 case AT_FULLAUTH_ID_REQ
:
197 DBG1(DBG_IKE
, "server requested %N, sending '%Y'",
198 simaka_attribute_names
, type
, this->peer
);
199 /* we reply with our permanent identity in any case */
202 if (!simaka_attribute_skippable(type
))
204 *out
= create_client_error(this, in
->get_identifier(in
));
205 enumerator
->destroy(enumerator
);
211 enumerator
->destroy(enumerator
);
213 message
= simaka_message_create(FALSE
, in
->get_identifier(in
), EAP_AKA
,
214 AKA_IDENTITY
, this->crypto
);
215 message
->add_attribute(message
, AT_IDENTITY
,
216 this->peer
->get_encoding(this->peer
));
217 *out
= message
->generate(message
, chunk_empty
);
218 message
->destroy(message
);
223 * Process an EAP-AKA/Request/Notification message
225 static status_t
process_notification(private_eap_aka_peer_t
*this,
226 simaka_message_t
*in
, eap_payload_t
**out
)
228 simaka_message_t
*message
;
229 enumerator_t
*enumerator
;
230 simaka_attribute_t type
;
234 enumerator
= in
->create_attribute_enumerator(in
);
235 while (enumerator
->enumerate(enumerator
, &type
, &data
))
237 if (type
== AT_NOTIFICATION
)
241 memcpy(&code
, data
.ptr
, sizeof(code
));
244 /* test success bit */
245 if (!(data
.ptr
[0] & 0x80))
248 DBG1(DBG_IKE
, "received EAP-AKA notification error '%N'",
249 simaka_notification_names
, code
);
253 DBG1(DBG_IKE
, "received EAP-AKA notification '%N'",
254 simaka_notification_names
, code
);
257 else if (!simaka_attribute_skippable(type
))
263 enumerator
->destroy(enumerator
);
266 { /* empty notification reply */
267 message
= simaka_message_create(FALSE
, in
->get_identifier(in
), EAP_AKA
,
268 AKA_NOTIFICATION
, this->crypto
);
269 *out
= message
->generate(message
, chunk_empty
);
270 message
->destroy(message
);
274 *out
= create_client_error(this, in
->get_identifier(in
));
281 * Implementation of eap_method_t.process
283 static status_t
process(private_eap_aka_peer_t
*this,
284 eap_payload_t
*in
, eap_payload_t
**out
)
286 simaka_message_t
*message
;
289 message
= simaka_message_create_from_payload(in
, this->crypto
);
292 *out
= create_client_error(this, in
->get_identifier(in
));
295 if (!message
->parse(message
))
297 message
->destroy(message
);
298 *out
= create_client_error(this, in
->get_identifier(in
));
301 switch (message
->get_subtype(message
))
304 status
= process_identity(this, message
, out
);
307 status
= process_challenge(this, message
, out
);
309 case AKA_NOTIFICATION
:
310 status
= process_notification(this, message
, out
);
313 DBG1(DBG_IKE
, "unable to process EAP-AKA subtype %N",
314 simaka_subtype_names
, message
->get_subtype(message
));
315 *out
= create_client_error(this, in
->get_identifier(in
));
319 message
->destroy(message
);
324 * Implementation of eap_method_t.initiate
326 static status_t
initiate(private_eap_aka_peer_t
*this, eap_payload_t
**out
)
328 /* peer never initiates */
333 * Implementation of eap_method_t.get_type.
335 static eap_type_t
get_type(private_eap_aka_peer_t
*this, u_int32_t
*vendor
)
342 * Implementation of eap_method_t.get_msk.
344 static status_t
get_msk(private_eap_aka_peer_t
*this, chunk_t
*msk
)
355 * Implementation of eap_method_t.is_mutual.
357 static bool is_mutual(private_eap_aka_peer_t
*this)
363 * Implementation of eap_method_t.destroy.
365 static void destroy(private_eap_aka_peer_t
*this)
367 this->crypto
->destroy(this->crypto
);
368 this->peer
->destroy(this->peer
);
374 * Described in header.
376 eap_aka_peer_t
*eap_aka_peer_create(identification_t
*server
,
377 identification_t
*peer
)
379 private_eap_aka_peer_t
*this = malloc_thing(private_eap_aka_peer_t
);
381 this->public.interface
.initiate
= (status_t(*)(eap_method_t
*,eap_payload_t
**))initiate
;
382 this->public.interface
.process
= (status_t(*)(eap_method_t
*,eap_payload_t
*,eap_payload_t
**))process
;
383 this->public.interface
.get_type
= (eap_type_t(*)(eap_method_t
*,u_int32_t
*))get_type
;
384 this->public.interface
.is_mutual
= (bool(*)(eap_method_t
*))is_mutual
;
385 this->public.interface
.get_msk
= (status_t(*)(eap_method_t
*,chunk_t
*))get_msk
;
386 this->public.interface
.destroy
= (void(*)(eap_method_t
*))destroy
;
388 this->crypto
= simaka_crypto_create();
394 this->peer
= peer
->clone(peer
);
395 this->msk
= chunk_empty
;
397 return &this->public;