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
30 ENUM(sim_subtype_names
, SIM_START
, SIM_CLIENT_ERROR
,
38 ENUM_BEGIN(sim_attribute_names
, AT_END
, AT_CLIENT_ERROR_CODE
,
50 "AT_PERMANENT_ID_REQ",
56 "AT_SELECTED_VERSION",
60 "AT_COUNTER_TOO_SMALL",
62 "AT_CLIENT_ERROR_CODE");
63 ENUM_NEXT(sim_attribute_names
, AT_IV
, AT_RESULT_IND
, AT_CLIENT_ERROR_CODE
,
71 ENUM_END(sim_attribute_names
, AT_RESULT_IND
);
74 typedef struct private_eap_sim_t private_eap_sim_t
;
77 * Private data of an eap_sim_t object.
79 struct private_eap_sim_t
{
82 * Public authenticator_t interface.
89 identification_t
*peer
;
92 * SIM cardreader function loaded from library
97 * handle of the loaded library
102 * version this implementation uses
107 * version list received from server
109 chunk_t version_list
;
112 * Nonce value used in AT_NONCE_MT
117 * k_encr key derived from MK
122 * k_auth key derived from MK, used for AT_MAC verification
127 * MSK, used for EAP-SIM based IKEv2 authentication
132 * EMSK, extendes MSK for further uses
137 /** length of the AT_NONCE_MT nonce value */
139 /** length of the AT_MAC value */
141 /** length of the AT_RAND value */
143 /** length of the k_encr key */
145 /** length of the k_auth key */
147 /** length of the MSK */
149 /** length of the EMSK */
152 /* client error codes used in AT_CLIENT_ERROR_CODE */
153 char client_error_general_buf
[] = {0x00, 0x01};
154 char client_error_unsupported_buf
[] = {0x00, 0x02};
155 char client_error_insufficient_buf
[] = {0x00, 0x03};
156 char client_error_notfresh_buf
[] = {0x00, 0x04};
157 chunk_t client_error_general
= chunk_from_buf(client_error_general_buf
);
158 chunk_t client_error_unsupported
= chunk_from_buf(client_error_unsupported_buf
);
159 chunk_t client_error_insufficient
= chunk_from_buf(client_error_insufficient_buf
);
160 chunk_t client_error_notfresh
= chunk_from_buf(client_error_notfresh_buf
);
163 * Read EAP and EAP-SIM header, return SIM type
165 static sim_subtype_t
read_header(chunk_t
*message
)
169 if (message
->len
< 8)
171 *message
= chunk_empty
;
174 type
= *(message
->ptr
+ 5);
175 *message
= chunk_skip(*message
, 8);
180 * read the next attribute from the chunk data
182 static sim_attribute_t
read_attribute(chunk_t
*message
, chunk_t
*data
)
184 sim_attribute_t attribute
;
187 DBG3(DBG_IKE
, "reading attribute from %B", message
);
189 if (message
->len
< 2)
193 attribute
= *message
->ptr
++;
194 length
= *message
->ptr
++ * 4 - 2;
196 DBG3(DBG_IKE
, "found attribute %N with length %d",
197 sim_attribute_names
, attribute
, length
);
199 if (length
> message
->len
)
204 data
->ptr
= message
->ptr
;
205 *message
= chunk_skip(*message
, length
);
210 * Build an EAP-SIM payload using a variable length attribute list.
211 * The variable argument takes a sim_attribute_t followed by its data in a chunk.
213 static eap_payload_t
*build_payload(private_eap_sim_t
*this, u_int8_t identifier
,
214 sim_subtype_t type
, ...)
216 chunk_t message
= chunk_alloca(512);
217 chunk_t pos
= message
;
218 eap_payload_t
*payload
;
220 sim_attribute_t attr
;
221 u_int8_t
*mac_pos
= NULL
;
222 chunk_t mac_data
= chunk_empty
;
224 /* write EAP header, skip length bytes */
225 *pos
.ptr
++ = EAP_RESPONSE
;
226 *pos
.ptr
++ = identifier
;
229 /* write SIM header with type and subtype, zero reserved bytes */
230 *pos
.ptr
++ = EAP_SIM
;
236 va_start(args
, type
);
237 while ((attr
= va_arg(args
, sim_attribute_t
)) != AT_END
)
239 chunk_t data
= va_arg(args
, chunk_t
);
241 DBG3(DBG_IKE
, "building %N %B", sim_attribute_names
, attr
, &data
);
243 /* write attribute header */
249 case AT_CLIENT_ERROR_CODE
:
250 case AT_SELECTED_VERSION
:
252 *pos
.ptr
= data
.len
/4 + 1;
253 pos
= chunk_skip(pos
, 1);
254 memcpy(pos
.ptr
, data
.ptr
, data
.len
);
255 pos
= chunk_skip(pos
, data
.len
);
260 /* align up to four byte */
263 chunk_t tmp
= chunk_alloca((data
.len
/4)*4 + 4);
264 memset(tmp
.ptr
, 0, tmp
.len
);
265 memcpy(tmp
.ptr
, data
.ptr
, data
.len
);
268 *pos
.ptr
= data
.len
/4 + 1;
269 pos
= chunk_skip(pos
, 1);
270 /* actual length in bytes */
271 *(u_int16_t
*)pos
.ptr
= htons(data
.len
);
272 pos
= chunk_skip(pos
, sizeof(u_int16_t
));
273 memcpy(pos
.ptr
, data
.ptr
, data
.len
);
274 pos
= chunk_skip(pos
, data
.len
);
279 *pos
.ptr
= data
.len
/4 + 1;
280 pos
= chunk_skip(pos
, 1);
281 memset(pos
.ptr
, 0, 2);
282 pos
= chunk_skip(pos
, 2);
283 memcpy(pos
.ptr
, data
.ptr
, data
.len
);
284 pos
= chunk_skip(pos
, data
.len
);
289 *pos
.ptr
++ = 5; pos
.len
--;
290 *pos
.ptr
++ = 0; pos
.len
--;
291 *pos
.ptr
++ = 0; pos
.len
--;
293 memset(mac_pos
, 0, MAC_LEN
);
294 pos
= chunk_skip(pos
, MAC_LEN
);
300 *pos
.ptr
++ = data
.len
/4 + 1; pos
.len
--;
301 *pos
.ptr
++ = 0; pos
.len
--;
302 *pos
.ptr
++ = 0; pos
.len
--;
303 memcpy(pos
.ptr
, data
.ptr
, data
.len
);
304 pos
= chunk_skip(pos
, data
.len
);
308 DBG1(DBG_IKE
, "no rule to build EAP_SIM attribute %N, skipped",
309 sim_attribute_names
, attr
);
315 /* calculate message length, write into header */
316 message
.len
= pos
.ptr
- message
.ptr
;
317 *(u_int16_t
*)(message
.ptr
+ 2) = htons(message
.len
);
319 /* create MAC if AT_MAC attribte was included. Append supplied va_arg
320 * chunk mac_data to "to-sign" chunk */
323 signer_t
*signer
= signer_create(AUTH_HMAC_SHA1_128
);
324 signer
->set_key(signer
, this->k_auth
);
325 mac_data
= chunk_cata("cc", message
, mac_data
);
326 signer
->get_signature(signer
, mac_data
, mac_pos
);
327 DBG3(DBG_IKE
, "AT_MAC signature of %B\n is %b",
328 &mac_data
, mac_pos
, MAC_LEN
);
329 signer
->destroy(signer
);
332 payload
= eap_payload_create_data(message
);
334 DBG3(DBG_IKE
, "created EAP message %B", &message
);
339 * process an EAP-SIM/Request/Start message
341 static status_t
process_start(private_eap_sim_t
*this, eap_payload_t
*in
,
344 chunk_t message
, data
;
345 sim_attribute_t attribute
, include_id
= AT_END
;
348 identifier
= in
->get_identifier(in
);
349 message
= in
->get_data(in
);
350 read_header(&message
);
352 while ((attribute
= read_attribute(&message
, &data
)) != AT_END
)
356 case AT_VERSION_LIST
:
358 /* check if server supports our implementation */
362 /* read actual length first */
363 data
.len
= min(data
.len
, ntohs(*(u_int16_t
*)data
.ptr
) + 2);
364 data
= chunk_skip(data
, 2);
365 chunk_free(&this->version_list
);
366 this->version_list
= chunk_clone(data
);
367 while (data
.len
>= this->version
.len
)
369 if (memeq(data
.ptr
, this->version
.ptr
, this->version
.len
))
374 data
= chunk_skip(data
, this->version
.len
);
379 DBG1(DBG_IKE
, "server does not support EAP_SIM "
380 "version number %#B", &this->version
);
381 *out
= build_payload(this, identifier
, SIM_CLIENT_ERROR
,
382 AT_CLIENT_ERROR_CODE
, client_error_unsupported
,
388 case AT_PERMANENT_ID_REQ
:
389 case AT_FULLAUTH_ID_REQ
:
391 /* only include AT_IDENTITY if requested */
392 include_id
= AT_IDENTITY
;
395 DBG1(DBG_IKE
, "ignoring EAP_SIM attribute %N",
396 sim_attribute_names
, attribute
);
401 /* build payload. If "include_id" is AT_END, AT_IDENTITY is ommited */
402 *out
= build_payload(this, identifier
, SIM_START
,
403 AT_SELECTED_VERSION
, this->version
,
404 AT_NONCE_MT
, this->nonce
,
405 include_id
, this->peer
->get_encoding(this->peer
),
411 * process an EAP-SIM/Request/Challenge message
413 static status_t
process_challenge(private_eap_sim_t
*this, eap_payload_t
*in
,
416 chunk_t message
, data
, tmp
, kcs
, kc
, sreses
, sres
, mk
;
417 sim_attribute_t attribute
;
418 u_int8_t identifier
, i
;
419 chunk_t mac
= chunk_empty
, rands
= chunk_empty
;
424 identifier
= in
->get_identifier(in
);
425 message
= in
->get_data(in
);
426 read_header(&message
);
428 while ((attribute
= read_attribute(&message
, &data
)) != AT_END
)
434 rands
= chunk_skip(data
, 2);
439 /* backup MAC, zero it inline for later verification */
440 data
= chunk_skip(data
, 2);
441 mac
= chunk_clonea(data
);
442 memset(data
.ptr
, 0, data
.len
);
446 DBG1(DBG_IKE
, "ignoring EAP_SIM attribute %N",
447 sim_attribute_names
, attribute
);
452 /* excepting two or three RAND, each 16 bytes. We require two valid
453 * and different RANDs */
454 if ((rands
.len
!= 2 * RAND_LEN
&& rands
.len
!= 3 * RAND_LEN
) ||
455 memeq(rands
.ptr
, rands
.ptr
+ RAND_LEN
, RAND_LEN
))
457 DBG1(DBG_IKE
, "no valid AT_RAND received");
458 *out
= build_payload(this, identifier
, SIM_CLIENT_ERROR
,
459 AT_CLIENT_ERROR_CODE
, client_error_insufficient
,
463 if (mac
.len
!= MAC_LEN
)
465 DBG1(DBG_IKE
, "no valid AT_MAC received");
466 *out
= build_payload(this, identifier
, SIM_CLIENT_ERROR
,
467 AT_CLIENT_ERROR_CODE
, client_error_general
,
472 /* get two or three KCs/SRESes from SIM using RANDs */
473 kcs
= kc
= chunk_alloca(rands
.len
/ 2);
474 sreses
= sres
= chunk_alloca(rands
.len
/ 4);
475 while (rands
.len
> 0)
477 int kc_len
= kc
.len
, sres_len
= sres
.len
;
479 if (this->alg(rands
.ptr
, RAND_LEN
, sres
.ptr
, &sres_len
, kc
.ptr
, &kc_len
))
481 DBG1(DBG_IKE
, "unable to get triplets from SIM");
482 *out
= build_payload(this, identifier
, SIM_CLIENT_ERROR
,
483 AT_CLIENT_ERROR_CODE
, client_error_general
,
487 DBG3(DBG_IKE
, "got triplet for RAND %b\n Kc %b\n SRES %b",
488 rands
.ptr
, RAND_LEN
, sres
.ptr
, sres_len
, kc
.ptr
, kc_len
);
489 kc
= chunk_skip(kc
, kc_len
);
490 sres
= chunk_skip(sres
, sres_len
);
491 rands
= chunk_skip(rands
, RAND_LEN
);
494 /* build MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
495 tmp
= chunk_cata("ccccc", this->peer
->get_encoding(this->peer
), kcs
,
496 this->nonce
, this->version_list
, this->version
);
497 hasher
= hasher_create(HASH_SHA1
);
498 mk
= chunk_alloca(hasher
->get_hash_size(hasher
));
499 hasher
->get_hash(hasher
, tmp
, mk
.ptr
);
500 hasher
->destroy(hasher
);
501 DBG3(DBG_IKE
, "MK = SHA1(%B\n) = %B", &tmp
, &mk
);
503 /* K_encr | K_auth | MSK | EMSK = prf() | prf() | prf() | prf()
504 * FIPS PRF has 320 bit block size, we need 160 byte for keys
505 * => run prf four times */
506 prf
= prf_create(PRF_FIPS_SHA1_160
);
507 prf
->set_key(prf
, mk
);
508 tmp
= chunk_alloca(prf
->get_block_size(prf
) * 4);
509 for (i
= 0; i
< 4; i
++)
511 prf
->get_bytes(prf
, chunk_empty
, tmp
.ptr
+ tmp
.len
/ 4 * i
);
514 chunk_free(&this->k_encr
);
515 chunk_free(&this->k_auth
);
516 chunk_free(&this->msk
);
517 chunk_free(&this->emsk
);
518 chunk_split(tmp
, "aaaa", KENCR_LEN
, &this->k_encr
, KAUTH_LEN
, &this->k_auth
,
519 MSK_LEN
, &this->msk
, EMSK_LEN
, &this->emsk
);
520 DBG3(DBG_IKE
, "K_encr %B\nK_auth %B\nMSK %B\nEMSK %B",
521 &this->k_encr
, &this->k_auth
, &this->msk
, &this->emsk
);
523 /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT" */
524 signer
= signer_create(AUTH_HMAC_SHA1_128
);
525 signer
->set_key(signer
, this->k_auth
);
526 tmp
= chunk_cata("cc", in
->get_data(in
), this->nonce
);
527 if (!signer
->verify_signature(signer
, tmp
, mac
))
529 DBG1(DBG_IKE
, "AT_MAC verification failed");
530 signer
->destroy(signer
);
531 *out
= build_payload(this, identifier
, SIM_CLIENT_ERROR
,
532 AT_CLIENT_ERROR_CODE
, client_error_general
,
536 signer
->destroy(signer
);
538 /* build response, AT_MAC is built over "EAP packet | n*SRES" */
539 *out
= build_payload(this, identifier
, SIM_CHALLENGE
,
546 * Implementation of eap_method_t.process for the peer
548 static status_t
process(private_eap_sim_t
*this,
549 eap_payload_t
*in
, eap_payload_t
**out
)
554 message
= in
->get_data(in
);
555 type
= read_header(&message
);
560 return process_start(this, in
, out
);
562 return process_challenge(this, in
, out
);
564 DBG1(DBG_IKE
, "unable to process EAP_SIM subtype %N",
565 sim_subtype_names
, type
);
566 *out
= build_payload(this, in
->get_identifier(in
), SIM_CLIENT_ERROR
,
567 AT_CLIENT_ERROR_CODE
, client_error_general
, AT_END
);
573 * Implementation of eap_method_t.initiate for the peer
575 static status_t
initiate(private_eap_sim_t
*this, eap_payload_t
**out
)
577 /* peer never initiates */
582 * Implementation of eap_method_t.get_type.
584 static eap_type_t
get_type(private_eap_sim_t
*this)
590 * Implementation of eap_method_t.get_msk.
592 static status_t
get_msk(private_eap_sim_t
*this, chunk_t
*msk
)
603 * Implementation of eap_method_t.is_mutual.
605 static bool is_mutual(private_eap_sim_t
*this)
611 * Implementation of eap_method_t.destroy.
613 static void destroy(private_eap_sim_t
*this)
615 dlclose(this->handle
);
616 chunk_free(&this->nonce
);
617 chunk_free(&this->version_list
);
618 chunk_free(&this->k_auth
);
619 chunk_free(&this->k_encr
);
620 chunk_free(&this->msk
);
621 chunk_free(&this->emsk
);
626 * Described in header.
628 eap_sim_t
*eap_create(eap_role_t role
,
629 identification_t
*server
, identification_t
*peer
)
631 private_eap_sim_t
*this;
632 randomizer_t
*randomizer
;
633 static char version
[] = {0x00,0x01};
635 if (role
!= EAP_PEER
)
639 this = malloc_thing(private_eap_sim_t
);
641 this->handle
= dlopen(SIM_READER_LIB
, RTLD_LAZY
);
642 if (this->handle
== NULL
)
644 DBG1(DBG_IKE
, "unable to open SIM reader '%s'", SIM_READER_LIB
);
648 this->alg
= dlsym(this->handle
, SIM_READER_ALG
);
649 if (this->alg
== NULL
)
651 DBG1(DBG_IKE
, "unable to open SIM reader function '%s' in '%s'",
652 SIM_READER_ALG
, SIM_READER_LIB
);
653 dlclose(this->handle
);
658 randomizer
= randomizer_create();
659 if (randomizer
->allocate_pseudo_random_bytes(randomizer
, NONCE_LEN
,
662 DBG1(DBG_IKE
, "unable to generate NONCE for EAP_SIM");
663 randomizer
->destroy(randomizer
);
667 randomizer
->destroy(randomizer
);
669 /* public functions */
670 this->public.eap_method_interface
.initiate
= (status_t(*)(eap_method_t
*,eap_payload_t
**))initiate
;
671 this->public.eap_method_interface
.process
= (status_t(*)(eap_method_t
*,eap_payload_t
*,eap_payload_t
**))process
;
672 this->public.eap_method_interface
.get_type
= (eap_type_t(*)(eap_method_t
*))get_type
;
673 this->public.eap_method_interface
.is_mutual
= (bool(*)(eap_method_t
*))is_mutual
;
674 this->public.eap_method_interface
.get_msk
= (status_t(*)(eap_method_t
*,chunk_t
*))get_msk
;
675 this->public.eap_method_interface
.destroy
= (void(*)(eap_method_t
*))destroy
;
679 this->version
.ptr
= version
;
680 this->version
.len
= sizeof(version
);
681 this->version_list
= chunk_empty
;
682 this->k_auth
= chunk_empty
;
683 this->k_encr
= chunk_empty
;
684 this->msk
= chunk_empty
;
685 this->emsk
= chunk_empty
;
687 return &this->public;