4 * @brief Implementation of eap_sim_t.
9 * Copyright (C) 2007 Martin Willi
10 * Hochschule fuer Technik Rapperswil
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
32 ENUM(sim_subtype_names
, SIM_START
, SIM_CLIENT_ERROR
,
40 ENUM_BEGIN(sim_attribute_names
, AT_END
, AT_CLIENT_ERROR_CODE
,
52 "AT_PERMANENT_ID_REQ",
58 "AT_SELECTED_VERSION",
62 "AT_COUNTER_TOO_SMALL",
64 "AT_CLIENT_ERROR_CODE");
65 ENUM_NEXT(sim_attribute_names
, AT_IV
, AT_RESULT_IND
, AT_CLIENT_ERROR_CODE
,
73 ENUM_END(sim_attribute_names
, AT_RESULT_IND
);
76 typedef struct private_eap_sim_t private_eap_sim_t
;
79 * Private data of an eap_sim_t object.
81 struct private_eap_sim_t
{
84 * Public authenticator_t interface.
91 identification_t
*peer
;
94 * SIM cardreader function loaded from library
99 * handle of the loaded library
104 * how many times we try to authenticate
109 * version this implementation uses
114 * version list received from server
116 chunk_t version_list
;
119 * Nonce value used in AT_NONCE_MT
124 * k_encr key derived from MK
129 * k_auth key derived from MK, used for AT_MAC verification
134 * MSK, used for EAP-SIM based IKEv2 authentication
139 * EMSK, extendes MSK for further uses
144 /** length of the AT_NONCE_MT nonce value */
146 /** length of the AT_MAC value */
148 /** length of the AT_RAND value */
150 /** length of the k_encr key */
152 /** length of the k_auth key */
154 /** length of the MSK */
156 /** length of the EMSK */
159 /* client error codes used in AT_CLIENT_ERROR_CODE */
160 char client_error_general_buf
[] = {0x00, 0x01};
161 char client_error_unsupported_buf
[] = {0x00, 0x02};
162 char client_error_insufficient_buf
[] = {0x00, 0x03};
163 char client_error_notfresh_buf
[] = {0x00, 0x04};
164 chunk_t client_error_general
= chunk_from_buf(client_error_general_buf
);
165 chunk_t client_error_unsupported
= chunk_from_buf(client_error_unsupported_buf
);
166 chunk_t client_error_insufficient
= chunk_from_buf(client_error_insufficient_buf
);
167 chunk_t client_error_notfresh
= chunk_from_buf(client_error_notfresh_buf
);
170 * Read EAP and EAP-SIM header, return SIM type
172 static sim_subtype_t
read_header(chunk_t
*message
)
176 if (message
->len
< 8)
178 *message
= chunk_empty
;
181 type
= *(message
->ptr
+ 5);
182 *message
= chunk_skip(*message
, 8);
187 * read the next attribute from the chunk data
189 static sim_attribute_t
read_attribute(chunk_t
*message
, chunk_t
*data
)
191 sim_attribute_t attribute
;
194 DBG3(DBG_IKE
, "reading attribute from %B", message
);
196 if (message
->len
< 2)
200 attribute
= *message
->ptr
++;
201 length
= *message
->ptr
++ * 4 - 2;
203 DBG3(DBG_IKE
, "found attribute %N with length %d",
204 sim_attribute_names
, attribute
, length
);
206 if (length
> message
->len
)
211 data
->ptr
= message
->ptr
;
212 *message
= chunk_skip(*message
, length
);
217 * Build an EAP-SIM payload using a variable length attribute list.
218 * The variable argument takes a sim_attribute_t followed by its data in a chunk.
220 static eap_payload_t
*build_payload(private_eap_sim_t
*this, u_int8_t identifier
,
221 sim_subtype_t type
, ...)
223 chunk_t message
= chunk_alloca(512);
224 chunk_t pos
= message
;
225 eap_payload_t
*payload
;
227 sim_attribute_t attr
;
228 u_int8_t
*mac_pos
= NULL
;
229 chunk_t mac_data
= chunk_empty
;
231 /* write EAP header, skip length bytes */
232 *pos
.ptr
++ = EAP_RESPONSE
;
233 *pos
.ptr
++ = identifier
;
236 /* write SIM header with type and subtype, zero reserved bytes */
237 *pos
.ptr
++ = EAP_SIM
;
243 va_start(args
, type
);
244 while ((attr
= va_arg(args
, sim_attribute_t
)) != AT_END
)
246 chunk_t data
= va_arg(args
, chunk_t
);
248 DBG3(DBG_IKE
, "building %N %B", sim_attribute_names
, attr
, &data
);
250 /* write attribute header */
256 case AT_CLIENT_ERROR_CODE
:
257 case AT_SELECTED_VERSION
:
259 *pos
.ptr
= data
.len
/4 + 1;
260 pos
= chunk_skip(pos
, 1);
261 memcpy(pos
.ptr
, data
.ptr
, data
.len
);
262 pos
= chunk_skip(pos
, data
.len
);
267 /* align up to four byte */
270 chunk_t tmp
= chunk_alloca((data
.len
/4)*4 + 4);
271 memset(tmp
.ptr
, 0, tmp
.len
);
272 memcpy(tmp
.ptr
, data
.ptr
, data
.len
);
275 *pos
.ptr
= data
.len
/4 + 1;
276 pos
= chunk_skip(pos
, 1);
277 /* actual length in bytes */
278 *(u_int16_t
*)pos
.ptr
= htons(data
.len
);
279 pos
= chunk_skip(pos
, sizeof(u_int16_t
));
280 memcpy(pos
.ptr
, data
.ptr
, data
.len
);
281 pos
= chunk_skip(pos
, data
.len
);
286 *pos
.ptr
= data
.len
/4 + 1;
287 pos
= chunk_skip(pos
, 1);
288 memset(pos
.ptr
, 0, 2);
289 pos
= chunk_skip(pos
, 2);
290 memcpy(pos
.ptr
, data
.ptr
, data
.len
);
291 pos
= chunk_skip(pos
, data
.len
);
296 *pos
.ptr
++ = 5; pos
.len
--;
297 *pos
.ptr
++ = 0; pos
.len
--;
298 *pos
.ptr
++ = 0; pos
.len
--;
300 memset(mac_pos
, 0, MAC_LEN
);
301 pos
= chunk_skip(pos
, MAC_LEN
);
307 *pos
.ptr
++ = data
.len
/4 + 1; pos
.len
--;
308 *pos
.ptr
++ = 0; pos
.len
--;
309 *pos
.ptr
++ = 0; pos
.len
--;
310 memcpy(pos
.ptr
, data
.ptr
, data
.len
);
311 pos
= chunk_skip(pos
, data
.len
);
315 DBG1(DBG_IKE
, "no rule to build EAP_SIM attribute %N, skipped",
316 sim_attribute_names
, attr
);
322 /* calculate message length, write into header */
323 message
.len
= pos
.ptr
- message
.ptr
;
324 *(u_int16_t
*)(message
.ptr
+ 2) = htons(message
.len
);
326 /* create MAC if AT_MAC attribte was included. Append supplied va_arg
327 * chunk mac_data to "to-sign" chunk */
330 signer_t
*signer
= signer_create(AUTH_HMAC_SHA1_128
);
331 signer
->set_key(signer
, this->k_auth
);
332 mac_data
= chunk_cata("cc", message
, mac_data
);
333 signer
->get_signature(signer
, mac_data
, mac_pos
);
334 DBG3(DBG_IKE
, "AT_MAC signature of %B\n is %b",
335 &mac_data
, mac_pos
, MAC_LEN
);
336 signer
->destroy(signer
);
339 payload
= eap_payload_create_data(message
);
341 DBG3(DBG_IKE
, "created EAP message %B", &message
);
346 * process an EAP-SIM/Request/Start message
348 static status_t
process_start(private_eap_sim_t
*this, eap_payload_t
*in
,
351 chunk_t message
, data
;
352 sim_attribute_t attribute
, include_id
= AT_END
;
355 identifier
= in
->get_identifier(in
);
356 message
= in
->get_data(in
);
357 read_header(&message
);
359 while ((attribute
= read_attribute(&message
, &data
)) != AT_END
)
363 case AT_VERSION_LIST
:
365 /* check if server supports our implementation */
369 /* read actual length first */
370 data
.len
= min(data
.len
, ntohs(*(u_int16_t
*)data
.ptr
) + 2);
371 data
= chunk_skip(data
, 2);
372 chunk_free(&this->version_list
);
373 this->version_list
= chunk_clone(data
);
374 while (data
.len
>= this->version
.len
)
376 if (memeq(data
.ptr
, this->version
.ptr
, this->version
.len
))
381 data
= chunk_skip(data
, this->version
.len
);
386 DBG1(DBG_IKE
, "server does not support EAP_SIM "
387 "version number %#B", &this->version
);
388 *out
= build_payload(this, identifier
, SIM_CLIENT_ERROR
,
389 AT_CLIENT_ERROR_CODE
, client_error_unsupported
,
395 case AT_PERMANENT_ID_REQ
:
396 case AT_FULLAUTH_ID_REQ
:
398 /* only include AT_IDENTITY if requested */
399 include_id
= AT_IDENTITY
;
401 case AT_NOTIFICATION
:
406 code
= ntohs(*(u_int16_t
*)data
.ptr
);
408 if (code
<= 32767) /* no success bit */
410 DBG1(DBG_IKE
, "received %N error %d",
411 sim_attribute_names
, attribute
, code
);
412 *out
= build_payload(this,
413 in
->get_identifier(in
), SIM_CLIENT_ERROR
,
414 AT_CLIENT_ERROR_CODE
, client_error_general
,
420 DBG1(DBG_IKE
, "received %N code %d",
421 sim_attribute_names
, attribute
, code
);
426 DBG1(DBG_IKE
, "ignoring EAP_SIM attribute %N",
427 sim_attribute_names
, attribute
);
432 /* build payload. If "include_id" is AT_END, AT_IDENTITY is ommited */
433 *out
= build_payload(this, identifier
, SIM_START
,
434 AT_SELECTED_VERSION
, this->version
,
435 AT_NONCE_MT
, this->nonce
,
436 include_id
, this->peer
->get_encoding(this->peer
),
442 * process an EAP-SIM/Request/Challenge message
444 static status_t
process_challenge(private_eap_sim_t
*this, eap_payload_t
*in
,
447 chunk_t message
, data
, tmp
, kcs
, kc
, sreses
, sres
, mk
;
448 sim_attribute_t attribute
;
449 u_int8_t identifier
, i
;
450 chunk_t mac
= chunk_empty
, rands
= chunk_empty
;
455 if (this->tries
-- <= 0)
457 /* give up without notification. This hack is required as some buggy
458 * server implementations won't respect our client-error. */
462 identifier
= in
->get_identifier(in
);
463 message
= in
->get_data(in
);
464 read_header(&message
);
466 while ((attribute
= read_attribute(&message
, &data
)) != AT_END
)
472 rands
= chunk_skip(data
, 2);
477 /* backup MAC, zero it inline for later verification */
478 data
= chunk_skip(data
, 2);
479 mac
= chunk_clonea(data
);
480 memset(data
.ptr
, 0, data
.len
);
483 case AT_NOTIFICATION
:
488 code
= ntohs(*(u_int16_t
*)data
.ptr
);
490 if (code
<= 32767) /* no success bit */
492 DBG1(DBG_IKE
, "received %N error %d",
493 sim_attribute_names
, attribute
, code
);
494 *out
= build_payload(this,
495 in
->get_identifier(in
), SIM_CLIENT_ERROR
,
496 AT_CLIENT_ERROR_CODE
, client_error_general
,
502 DBG1(DBG_IKE
, "received %N code %d",
503 sim_attribute_names
, attribute
, code
);
508 DBG1(DBG_IKE
, "ignoring EAP_SIM attribute %N",
509 sim_attribute_names
, attribute
);
514 /* excepting two or three RAND, each 16 bytes. We require two valid
515 * and different RANDs */
516 if ((rands
.len
!= 2 * RAND_LEN
&& rands
.len
!= 3 * RAND_LEN
) ||
517 memeq(rands
.ptr
, rands
.ptr
+ RAND_LEN
, RAND_LEN
))
519 DBG1(DBG_IKE
, "no valid AT_RAND received");
520 *out
= build_payload(this, identifier
, SIM_CLIENT_ERROR
,
521 AT_CLIENT_ERROR_CODE
, client_error_insufficient
,
525 if (mac
.len
!= MAC_LEN
)
527 DBG1(DBG_IKE
, "no valid AT_MAC received");
528 *out
= build_payload(this, identifier
, SIM_CLIENT_ERROR
,
529 AT_CLIENT_ERROR_CODE
, client_error_general
,
534 /* get two or three KCs/SRESes from SIM using RANDs */
535 kcs
= kc
= chunk_alloca(rands
.len
/ 2);
536 sreses
= sres
= chunk_alloca(rands
.len
/ 4);
537 while (rands
.len
> 0)
539 int kc_len
= kc
.len
, sres_len
= sres
.len
;
541 if (this->alg(rands
.ptr
, RAND_LEN
, sres
.ptr
, &sres_len
, kc
.ptr
, &kc_len
))
543 DBG1(DBG_IKE
, "unable to get triplets from SIM");
544 *out
= build_payload(this, identifier
, SIM_CLIENT_ERROR
,
545 AT_CLIENT_ERROR_CODE
, client_error_general
,
549 DBG3(DBG_IKE
, "got triplet for RAND %b\n Kc %b\n SRES %b",
550 rands
.ptr
, RAND_LEN
, sres
.ptr
, sres_len
, kc
.ptr
, kc_len
);
551 kc
= chunk_skip(kc
, kc_len
);
552 sres
= chunk_skip(sres
, sres_len
);
553 rands
= chunk_skip(rands
, RAND_LEN
);
556 /* build MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
557 tmp
= chunk_cata("ccccc", this->peer
->get_encoding(this->peer
), kcs
,
558 this->nonce
, this->version_list
, this->version
);
559 hasher
= hasher_create(HASH_SHA1
);
560 mk
= chunk_alloca(hasher
->get_hash_size(hasher
));
561 hasher
->get_hash(hasher
, tmp
, mk
.ptr
);
562 hasher
->destroy(hasher
);
563 DBG3(DBG_IKE
, "MK = SHA1(%B\n) = %B", &tmp
, &mk
);
565 /* K_encr | K_auth | MSK | EMSK = prf() | prf() | prf() | prf()
566 * FIPS PRF has 320 bit block size, we need 160 byte for keys
567 * => run prf four times */
568 prf
= prf_create(PRF_FIPS_SHA1_160
);
569 prf
->set_key(prf
, mk
);
570 tmp
= chunk_alloca(prf
->get_block_size(prf
) * 4);
571 for (i
= 0; i
< 4; i
++)
573 prf
->get_bytes(prf
, chunk_empty
, tmp
.ptr
+ tmp
.len
/ 4 * i
);
576 chunk_free(&this->k_encr
);
577 chunk_free(&this->k_auth
);
578 chunk_free(&this->msk
);
579 chunk_free(&this->emsk
);
580 chunk_split(tmp
, "aaaa", KENCR_LEN
, &this->k_encr
, KAUTH_LEN
, &this->k_auth
,
581 MSK_LEN
, &this->msk
, EMSK_LEN
, &this->emsk
);
582 DBG3(DBG_IKE
, "K_encr %B\nK_auth %B\nMSK %B\nEMSK %B",
583 &this->k_encr
, &this->k_auth
, &this->msk
, &this->emsk
);
585 /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT" */
586 signer
= signer_create(AUTH_HMAC_SHA1_128
);
587 signer
->set_key(signer
, this->k_auth
);
588 tmp
= chunk_cata("cc", in
->get_data(in
), this->nonce
);
589 if (!signer
->verify_signature(signer
, tmp
, mac
))
591 DBG1(DBG_IKE
, "AT_MAC verification failed");
592 signer
->destroy(signer
);
593 *out
= build_payload(this, identifier
, SIM_CLIENT_ERROR
,
594 AT_CLIENT_ERROR_CODE
, client_error_general
,
598 signer
->destroy(signer
);
600 /* build response, AT_MAC is built over "EAP packet | n*SRES" */
601 *out
= build_payload(this, identifier
, SIM_CHALLENGE
,
608 * process an EAP-SIM/Request/Notification message
610 static status_t
process_notification(private_eap_sim_t
*this, eap_payload_t
*in
,
613 chunk_t message
, data
;
614 sim_attribute_t attribute
;
616 message
= in
->get_data(in
);
617 read_header(&message
);
619 while ((attribute
= read_attribute(&message
, &data
)) != AT_END
)
623 case AT_NOTIFICATION
:
628 code
= ntohs(*(u_int16_t
*)data
.ptr
);
630 if (code
<= 32767) /* no success bit */
632 DBG1(DBG_IKE
, "received %N error %d",
633 sim_attribute_names
, attribute
, code
);
634 *out
= build_payload(this,
635 in
->get_identifier(in
), SIM_CLIENT_ERROR
,
636 AT_CLIENT_ERROR_CODE
, client_error_general
,
642 DBG1(DBG_IKE
, "received %N code %d",
643 sim_attribute_names
, attribute
, code
);
648 DBG1(DBG_IKE
, "ignoring EAP_SIM attribute %N",
649 sim_attribute_names
, attribute
);
653 /* reply with empty notification */
654 *out
= build_payload(this, in
->get_identifier(in
), SIM_NOTIFICATION
, AT_END
);
660 * Implementation of eap_method_t.process for the peer
662 static status_t
process(private_eap_sim_t
*this,
663 eap_payload_t
*in
, eap_payload_t
**out
)
668 message
= in
->get_data(in
);
669 type
= read_header(&message
);
674 return process_start(this, in
, out
);
676 return process_challenge(this, in
, out
);
677 case SIM_NOTIFICATION
:
678 return process_notification(this, in
, out
);
680 DBG1(DBG_IKE
, "unable to process EAP_SIM subtype %N",
681 sim_subtype_names
, type
);
682 *out
= build_payload(this, in
->get_identifier(in
), SIM_CLIENT_ERROR
,
683 AT_CLIENT_ERROR_CODE
, client_error_general
, AT_END
);
689 * Implementation of eap_method_t.initiate for the peer
691 static status_t
initiate(private_eap_sim_t
*this, eap_payload_t
**out
)
693 /* peer never initiates */
698 * Implementation of eap_method_t.get_type.
700 static eap_type_t
get_type(private_eap_sim_t
*this)
706 * Implementation of eap_method_t.get_msk.
708 static status_t
get_msk(private_eap_sim_t
*this, chunk_t
*msk
)
719 * Implementation of eap_method_t.is_mutual.
721 static bool is_mutual(private_eap_sim_t
*this)
727 * Implementation of eap_method_t.destroy.
729 static void destroy(private_eap_sim_t
*this)
731 dlclose(this->handle
);
732 chunk_free(&this->nonce
);
733 chunk_free(&this->version_list
);
734 chunk_free(&this->k_auth
);
735 chunk_free(&this->k_encr
);
736 chunk_free(&this->msk
);
737 chunk_free(&this->emsk
);
742 * Described in header.
744 eap_sim_t
*eap_create(eap_role_t role
,
745 identification_t
*server
, identification_t
*peer
)
747 private_eap_sim_t
*this;
748 randomizer_t
*randomizer
;
749 static char version
[] = {0x00,0x01};
751 if (role
!= EAP_PEER
)
755 this = malloc_thing(private_eap_sim_t
);
757 this->handle
= dlopen(SIM_READER_LIB
, RTLD_LAZY
);
758 if (this->handle
== NULL
)
760 DBG1(DBG_IKE
, "unable to open SIM reader '%s'", SIM_READER_LIB
);
764 this->alg
= dlsym(this->handle
, SIM_READER_ALG
);
765 if (this->alg
== NULL
)
767 DBG1(DBG_IKE
, "unable to open SIM reader function '%s' in '%s'",
768 SIM_READER_ALG
, SIM_READER_LIB
);
769 dlclose(this->handle
);
774 randomizer
= randomizer_create();
775 if (randomizer
->allocate_pseudo_random_bytes(randomizer
, NONCE_LEN
,
778 DBG1(DBG_IKE
, "unable to generate NONCE for EAP_SIM");
779 randomizer
->destroy(randomizer
);
783 randomizer
->destroy(randomizer
);
785 /* public functions */
786 this->public.eap_method_interface
.initiate
= (status_t(*)(eap_method_t
*,eap_payload_t
**))initiate
;
787 this->public.eap_method_interface
.process
= (status_t(*)(eap_method_t
*,eap_payload_t
*,eap_payload_t
**))process
;
788 this->public.eap_method_interface
.get_type
= (eap_type_t(*)(eap_method_t
*))get_type
;
789 this->public.eap_method_interface
.is_mutual
= (bool(*)(eap_method_t
*))is_mutual
;
790 this->public.eap_method_interface
.get_msk
= (status_t(*)(eap_method_t
*,chunk_t
*))get_msk
;
791 this->public.eap_method_interface
.destroy
= (void(*)(eap_method_t
*))destroy
;
795 this->tries
= MAX_TRIES
;
796 this->version
.ptr
= version
;
797 this->version
.len
= sizeof(version
);
798 this->version_list
= chunk_empty
;
799 this->k_auth
= chunk_empty
;
800 this->k_encr
= chunk_empty
;
801 this->msk
= chunk_empty
;
802 this->emsk
= chunk_empty
;
804 return &this->public;