2 * Copyright (C) 2007-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_sim_peer.h"
20 #include <simaka_message.h>
22 /* number of tries we do authenticate */
25 /* number of triplets for one authentication */
26 #define TRIPLET_COUNT 3
28 /** length of the AT_NONCE_MT nonce value */
31 typedef struct private_eap_sim_peer_t private_eap_sim_peer_t
;
34 * Private data of an eap_sim_peer_t object.
36 struct private_eap_sim_peer_t
{
39 * Public authenticator_t interface.
41 eap_sim_peer_t
public;
44 * permanent ID of peer
46 identification_t
*permanent
;
49 * Pseudonym identity the peer uses
51 identification_t
*pseudonym
;
54 * Reauthentication identity the peer uses
56 identification_t
*reauth
;
59 * EAP-SIM crypto helper
61 simaka_crypto_t
*crypto
;
64 * how many times we try to authenticate
69 * version list received from server
74 * Nonce value used in AT_NONCE_MT/AT_NONCE_S
79 * MSK, used for EAP-SIM based IKEv2 authentication
84 * Master key, if reauthentication is used
86 char mk
[HASH_SIZE_SHA1
];
89 * Counter value if reauthentication is used
94 /* version of SIM protocol we speak */
95 static chunk_t version
= chunk_from_chars(0x00,0x01);
98 * Create a SIM_CLIENT_ERROR
100 static eap_payload_t
* create_client_error(private_eap_sim_peer_t
*this,
101 u_int8_t identifier
, simaka_client_error_t code
)
103 simaka_message_t
*message
;
107 DBG1(DBG_IKE
, "sending client error '%N'", simaka_client_error_names
, code
);
109 message
= simaka_message_create(FALSE
, identifier
, EAP_SIM
,
110 SIM_CLIENT_ERROR
, this->crypto
);
111 encoded
= htons(code
);
112 message
->add_attribute(message
, AT_CLIENT_ERROR_CODE
,
113 chunk_create((char*)&encoded
, sizeof(encoded
)));
114 out
= message
->generate(message
, chunk_empty
);
115 message
->destroy(message
);
120 * process an EAP-SIM/Request/Start message
122 static status_t
process_start(private_eap_sim_peer_t
*this,
123 simaka_message_t
*in
, eap_payload_t
**out
)
125 simaka_message_t
*message
;
126 enumerator_t
*enumerator
;
127 simaka_attribute_t type
;
128 chunk_t data
, id
= chunk_empty
;
130 bool supported
= FALSE
;
131 simaka_attribute_t id_req
= 0;
133 /* reset previously uses reauthentication/pseudonym data */
134 this->crypto
->clear_keys(this->crypto
);
135 DESTROY_IF(this->pseudonym
);
136 this->pseudonym
= NULL
;
137 DESTROY_IF(this->reauth
);
140 enumerator
= in
->create_attribute_enumerator(in
);
141 while (enumerator
->enumerate(enumerator
, &type
, &data
))
145 case AT_VERSION_LIST
:
147 free(this->version_list
.ptr
);
148 this->version_list
= chunk_clone(data
);
149 while (data
.len
>= version
.len
)
151 if (memeq(data
.ptr
, version
.ptr
, version
.len
))
160 case AT_FULLAUTH_ID_REQ
:
161 case AT_PERMANENT_ID_REQ
:
165 if (!simaka_attribute_skippable(type
))
167 *out
= create_client_error(this, in
->get_identifier(in
),
168 SIM_UNABLE_TO_PROCESS
);
169 enumerator
->destroy(enumerator
);
175 enumerator
->destroy(enumerator
);
179 DBG1(DBG_IKE
, "server does not support EAP-SIM version number 1");
180 *out
= create_client_error(this, in
->get_identifier(in
),
181 SIM_UNSUPPORTED_VERSION
);
188 this->reauth
= charon
->sim
->card_get_reauth(charon
->sim
,
189 this->permanent
, this->mk
, &this->counter
);
192 id
= this->reauth
->get_encoding(this->reauth
);
196 case AT_FULLAUTH_ID_REQ
:
197 this->pseudonym
= charon
->sim
->card_get_pseudonym(charon
->sim
,
201 id
= this->pseudonym
->get_encoding(this->pseudonym
);
205 case AT_PERMANENT_ID_REQ
:
206 id
= this->permanent
->get_encoding(this->permanent
);
212 /* generate AT_NONCE_MT value */
213 rng
= this->crypto
->get_rng(this->crypto
);
214 free(this->nonce
.ptr
);
215 rng
->allocate_bytes(rng
, NONCE_LEN
, &this->nonce
);
217 message
= simaka_message_create(FALSE
, in
->get_identifier(in
), EAP_SIM
,
218 SIM_START
, this->crypto
);
221 message
->add_attribute(message
, AT_SELECTED_VERSION
, version
);
222 message
->add_attribute(message
, AT_NONCE_MT
, this->nonce
);
226 message
->add_attribute(message
, AT_IDENTITY
, id
);
228 *out
= message
->generate(message
, chunk_empty
);
229 message
->destroy(message
);
235 * process an EAP-SIM/Request/Challenge message
237 static status_t
process_challenge(private_eap_sim_peer_t
*this,
238 simaka_message_t
*in
, eap_payload_t
**out
)
240 simaka_message_t
*message
;
241 enumerator_t
*enumerator
;
242 simaka_attribute_t type
;
243 chunk_t data
, rands
= chunk_empty
, kcs
, kc
, sreses
, sres
, mk
;
244 identification_t
*id
;
246 if (this->tries
-- <= 0)
248 /* give up without notification. This hack is required as some buggy
249 * server implementations won't respect our client-error. */
253 enumerator
= in
->create_attribute_enumerator(in
);
254 while (enumerator
->enumerate(enumerator
, &type
, &data
))
262 if (!simaka_attribute_skippable(type
))
264 *out
= create_client_error(this, in
->get_identifier(in
),
265 SIM_UNABLE_TO_PROCESS
);
266 enumerator
->destroy(enumerator
);
272 enumerator
->destroy(enumerator
);
274 /* excepting two or three RAND, each 16 bytes. We require two valid
275 * and different RANDs */
276 if ((rands
.len
!= 2 * SIM_RAND_LEN
&& rands
.len
!= 3 * SIM_RAND_LEN
) ||
277 memeq(rands
.ptr
, rands
.ptr
+ SIM_RAND_LEN
, SIM_RAND_LEN
))
279 DBG1(DBG_IKE
, "no valid AT_RAND received");
280 *out
= create_client_error(this, in
->get_identifier(in
),
281 SIM_INSUFFICIENT_CHALLENGES
);
284 /* get two or three KCs/SRESes from SIM using RANDs */
285 kcs
= kc
= chunk_alloca(rands
.len
/ 2);
286 sreses
= sres
= chunk_alloca(rands
.len
/ 4);
287 while (rands
.len
>= SIM_RAND_LEN
)
289 if (!charon
->sim
->card_get_triplet(charon
->sim
, this->permanent
,
290 rands
.ptr
, sres
.ptr
, kc
.ptr
))
292 DBG1(DBG_IKE
, "unable to get EAP-SIM triplet");
293 *out
= create_client_error(this, in
->get_identifier(in
),
294 SIM_UNABLE_TO_PROCESS
);
297 DBG3(DBG_IKE
, "got triplet for RAND %b\n Kc %b\n SRES %b",
298 rands
.ptr
, SIM_RAND_LEN
, sres
.ptr
, SIM_SRES_LEN
, kc
.ptr
, SIM_KC_LEN
);
299 kc
= chunk_skip(kc
, SIM_KC_LEN
);
300 sres
= chunk_skip(sres
, SIM_SRES_LEN
);
301 rands
= chunk_skip(rands
, SIM_RAND_LEN
);
304 id
= this->permanent
;
307 id
= this->pseudonym
;
309 data
= chunk_cata("cccc", kcs
, this->nonce
, this->version_list
, version
);
311 this->msk
= this->crypto
->derive_keys_full(this->crypto
, id
, data
, &mk
);
312 memcpy(this->mk
, mk
.ptr
, mk
.len
);
315 /* Verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT", and
316 * parse() again after key derivation, reading encrypted attributes */
317 if (!in
->verify(in
, this->nonce
) || !in
->parse(in
))
319 *out
= create_client_error(this, in
->get_identifier(in
),
320 SIM_UNABLE_TO_PROCESS
);
324 enumerator
= in
->create_attribute_enumerator(in
);
325 while (enumerator
->enumerate(enumerator
, &type
, &data
))
329 case AT_NEXT_REAUTH_ID
:
331 id
= identification_create_from_data(data
);
332 charon
->sim
->card_set_reauth(charon
->sim
, this->permanent
, id
,
333 this->mk
, this->counter
);
336 case AT_NEXT_PSEUDONYM
:
337 id
= identification_create_from_data(data
);
338 charon
->sim
->card_set_pseudonym(charon
->sim
, this->permanent
, id
);
345 enumerator
->destroy(enumerator
);
347 /* build response with AT_MAC, built over "EAP packet | n*SRES" */
348 message
= simaka_message_create(FALSE
, in
->get_identifier(in
), EAP_SIM
,
349 SIM_CHALLENGE
, this->crypto
);
350 *out
= message
->generate(message
, sreses
);
351 message
->destroy(message
);
356 * Check if a received counter value is acceptable
358 static bool counter_too_small(private_eap_sim_peer_t
*this, chunk_t chunk
)
362 memcpy(&counter
, chunk
.ptr
, sizeof(counter
));
363 counter
= htons(counter
);
364 return counter
< this->counter
;
368 * process an EAP-SIM/Request/Re-Authentication message
370 static status_t
process_reauthentication(private_eap_sim_peer_t
*this,
371 simaka_message_t
*in
, eap_payload_t
**out
)
373 simaka_message_t
*message
;
374 enumerator_t
*enumerator
;
375 simaka_attribute_t type
;
376 chunk_t data
, counter
= chunk_empty
, nonce
= chunk_empty
, id
= chunk_empty
;
380 DBG1(DBG_IKE
, "received %N, but not expected",
381 simaka_subtype_names
, SIM_REAUTHENTICATION
);
382 *out
= create_client_error(this, in
->get_identifier(in
),
383 SIM_UNABLE_TO_PROCESS
);
387 this->crypto
->derive_keys_reauth(this->crypto
,
388 chunk_create(this->mk
, HASH_SIZE_SHA1
));
390 /* verify MAC and parse again with decryption key */
391 if (!in
->verify(in
, chunk_empty
) || !in
->parse(in
))
393 *out
= create_client_error(this, in
->get_identifier(in
),
394 SIM_UNABLE_TO_PROCESS
);
398 enumerator
= in
->create_attribute_enumerator(in
);
399 while (enumerator
->enumerate(enumerator
, &type
, &data
))
409 case AT_NEXT_REAUTH_ID
:
413 if (!simaka_attribute_skippable(type
))
415 *out
= create_client_error(this, in
->get_identifier(in
),
416 SIM_UNABLE_TO_PROCESS
);
417 enumerator
->destroy(enumerator
);
423 enumerator
->destroy(enumerator
);
425 if (!nonce
.len
|| !counter
.len
)
427 DBG1(DBG_IKE
, "EAP-SIM/Request/Re-Authentication message incomplete");
428 *out
= create_client_error(this, in
->get_identifier(in
),
429 SIM_UNABLE_TO_PROCESS
);
433 message
= simaka_message_create(FALSE
, in
->get_identifier(in
), EAP_SIM
,
434 SIM_REAUTHENTICATION
, this->crypto
);
435 if (counter_too_small(this, counter
))
437 DBG1(DBG_IKE
, "reauthentication counter too small");
438 message
->add_attribute(message
, AT_COUNTER_TOO_SMALL
, chunk_empty
);
443 this->msk
= this->crypto
->derive_keys_reauth_msk(this->crypto
,
444 this->reauth
, counter
, nonce
,
445 chunk_create(this->mk
, HASH_SIZE_SHA1
));
448 identification_t
*reauth
;
450 reauth
= identification_create_from_data(data
);
451 charon
->sim
->card_set_reauth(charon
->sim
, this->permanent
, reauth
,
452 this->mk
, this->counter
);
453 reauth
->destroy(reauth
);
456 message
->add_attribute(message
, AT_COUNTER
, counter
);
457 *out
= message
->generate(message
, nonce
);
458 message
->destroy(message
);
463 * process an EAP-SIM/Request/Notification message
465 static status_t
process_notification(private_eap_sim_peer_t
*this,
466 simaka_message_t
*in
, eap_payload_t
**out
)
468 simaka_message_t
*message
;
469 enumerator_t
*enumerator
;
470 simaka_attribute_t type
;
474 enumerator
= in
->create_attribute_enumerator(in
);
475 while (enumerator
->enumerate(enumerator
, &type
, &data
))
477 if (type
== AT_NOTIFICATION
)
481 memcpy(&code
, data
.ptr
, sizeof(code
));
484 /* test success bit */
485 if (!(data
.ptr
[0] & 0x80))
488 DBG1(DBG_IKE
, "received EAP-SIM notification error '%N'",
489 simaka_notification_names
, code
);
493 DBG1(DBG_IKE
, "received EAP-SIM notification '%N'",
494 simaka_notification_names
, code
);
497 else if (!simaka_attribute_skippable(type
))
503 enumerator
->destroy(enumerator
);
506 { /* empty notification reply */
507 message
= simaka_message_create(FALSE
, in
->get_identifier(in
), EAP_SIM
,
508 SIM_NOTIFICATION
, this->crypto
);
509 *out
= message
->generate(message
, chunk_empty
);
510 message
->destroy(message
);
514 *out
= create_client_error(this, in
->get_identifier(in
),
515 SIM_UNABLE_TO_PROCESS
);
521 * Implementation of eap_method_t.process
523 static status_t
process(private_eap_sim_peer_t
*this,
524 eap_payload_t
*in
, eap_payload_t
**out
)
526 simaka_message_t
*message
;
529 message
= simaka_message_create_from_payload(in
, this->crypto
);
532 *out
= create_client_error(this, in
->get_identifier(in
),
533 SIM_UNABLE_TO_PROCESS
);
536 if (!message
->parse(message
))
538 message
->destroy(message
);
539 *out
= create_client_error(this, in
->get_identifier(in
),
540 SIM_UNABLE_TO_PROCESS
);
543 switch (message
->get_subtype(message
))
546 status
= process_start(this, message
, out
);
549 status
= process_challenge(this, message
, out
);
551 case SIM_REAUTHENTICATION
:
552 status
= process_reauthentication(this, message
, out
);
554 case SIM_NOTIFICATION
:
555 status
= process_notification(this, message
, out
);
558 DBG1(DBG_IKE
, "unable to process EAP-SIM subtype %N",
559 simaka_subtype_names
, message
->get_subtype(message
));
560 *out
= create_client_error(this, in
->get_identifier(in
),
561 SIM_UNABLE_TO_PROCESS
);
565 message
->destroy(message
);
570 * Implementation of eap_method_t.initiate
572 static status_t
initiate(private_eap_sim_peer_t
*this, eap_payload_t
**out
)
574 /* peer never initiates */
579 * Implementation of eap_method_t.get_type.
581 static eap_type_t
get_type(private_eap_sim_peer_t
*this, u_int32_t
*vendor
)
588 * Implementation of eap_method_t.get_msk.
590 static status_t
get_msk(private_eap_sim_peer_t
*this, chunk_t
*msk
)
601 * Implementation of eap_method_t.is_mutual.
603 static bool is_mutual(private_eap_sim_peer_t
*this)
609 * Implementation of eap_method_t.destroy.
611 static void destroy(private_eap_sim_peer_t
*this)
613 this->permanent
->destroy(this->permanent
);
614 DESTROY_IF(this->pseudonym
);
615 DESTROY_IF(this->reauth
);
616 this->crypto
->destroy(this->crypto
);
617 free(this->version_list
.ptr
);
618 free(this->nonce
.ptr
);
624 * Described in header.
626 eap_sim_peer_t
*eap_sim_peer_create(identification_t
*server
,
627 identification_t
*peer
)
629 private_eap_sim_peer_t
*this = malloc_thing(private_eap_sim_peer_t
);
631 this->public.interface
.initiate
= (status_t(*)(eap_method_t
*,eap_payload_t
**))initiate
;
632 this->public.interface
.process
= (status_t(*)(eap_method_t
*,eap_payload_t
*,eap_payload_t
**))process
;
633 this->public.interface
.get_type
= (eap_type_t(*)(eap_method_t
*,u_int32_t
*))get_type
;
634 this->public.interface
.is_mutual
= (bool(*)(eap_method_t
*))is_mutual
;
635 this->public.interface
.get_msk
= (status_t(*)(eap_method_t
*,chunk_t
*))get_msk
;
636 this->public.interface
.destroy
= (void(*)(eap_method_t
*))destroy
;
638 this->crypto
= simaka_crypto_create();
644 this->permanent
= peer
->clone(peer
);
645 this->pseudonym
= NULL
;
647 this->tries
= MAX_TRIES
;
648 this->version_list
= chunk_empty
;
649 this->nonce
= chunk_empty
;
650 this->msk
= chunk_empty
;
652 return &this->public;