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 value */
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 *pos
.ptr
= data
.len
/4 + 1;
261 pos
= chunk_skip(pos
, 1);
262 /* actual length in bytes */
263 *(u_int16_t
*)pos
.ptr
= htons(data
.len
);
264 pos
= chunk_skip(pos
, sizeof(u_int16_t
));
265 memcpy(pos
.ptr
, data
.ptr
, data
.len
);
266 pos
= chunk_skip(pos
, data
.len
);
271 *pos
.ptr
= data
.len
/4 + 1;
272 pos
= chunk_skip(pos
, 1);
273 memset(pos
.ptr
, 0, 2);
274 pos
= chunk_skip(pos
, 2);
275 memcpy(pos
.ptr
, data
.ptr
, data
.len
);
276 pos
= chunk_skip(pos
, data
.len
);
281 *pos
.ptr
++ = 5; pos
.len
--;
282 *pos
.ptr
++ = 0; pos
.len
--;
283 *pos
.ptr
++ = 0; pos
.len
--;
285 memset(mac_pos
, 0, MAC_LEN
);
286 pos
= chunk_skip(pos
, MAC_LEN
);
292 *pos
.ptr
++ = data
.len
/4 + 1; pos
.len
--;
293 *pos
.ptr
++ = 0; pos
.len
--;
294 *pos
.ptr
++ = 0; pos
.len
--;
295 memcpy(pos
.ptr
, data
.ptr
, data
.len
);
296 pos
= chunk_skip(pos
, data
.len
);
300 DBG1(DBG_IKE
, "no rule to build EAP_SIM attribute %N, skipped",
301 sim_attribute_names
, attr
);
307 /* calculate message length, write into header */
308 message
.len
= pos
.ptr
- message
.ptr
;
309 *(u_int16_t
*)(message
.ptr
+ 2) = htons(message
.len
);
311 /* create MAC if AT_MAC attribte was included. Append supplied va_arg
312 * chunk mac_data to "to-sign" chunk */
315 signer_t
*signer
= signer_create(AUTH_HMAC_SHA1_128
);
316 signer
->set_key(signer
, this->k_auth
);
317 mac_data
= chunk_cata("cc", message
, mac_data
);
318 DBG3(DBG_IKE
, "AT_MAC signature of %B", &mac_data
);
319 DBG3(DBG_IKE
, "using k_auth %B", &this->k_auth
);
320 signer
->get_signature(signer
, mac_data
, mac_pos
);
321 DBG3(DBG_IKE
, "is %b", mac_pos
, MAC_LEN
);
322 signer
->destroy(signer
);
325 payload
= eap_payload_create_data(message
);
327 DBG3(DBG_IKE
, "created EAP message %B", &message
);
332 * process an EAP-SIM/Request/Start message
334 static status_t
process_start(private_eap_sim_t
*this, eap_payload_t
*in
,
337 chunk_t message
, data
;
338 sim_attribute_t attribute
, include_id
= AT_END
;
341 identifier
= in
->get_identifier(in
);
342 message
= in
->get_data(in
);
343 read_header(&message
);
345 while ((attribute
= read_attribute(&message
, &data
)) != AT_END
)
349 case AT_VERSION_LIST
:
351 /* check if server supports our implementation */
356 /* read actual length first */
357 data
.len
= min(data
.len
, ntohs(*(u_int16_t
*)data
.ptr
) + 2);
358 data
= chunk_skip(data
, 2);
359 chunk_free(&this->version_list
);
360 this->version_list
= chunk_clone(data
);
361 while (data
.len
>= this->version
.len
)
363 if (memeq(data
.ptr
, this->version
.ptr
, this->version
.len
))
368 data
= chunk_skip(data
, this->version
.len
);
373 DBG1(DBG_IKE
, "server does not support EAP_SIM "
374 "version number %#B", &this->version
);
375 *out
= build_payload(this, identifier
, SIM_CLIENT_ERROR
,
376 AT_CLIENT_ERROR_CODE
, client_error_unsupported
,
382 case AT_PERMANENT_ID_REQ
:
383 case AT_FULLAUTH_ID_REQ
:
385 /* only include AT_IDENTITY if requested */
386 include_id
= AT_IDENTITY
;
389 DBG1(DBG_IKE
, "ignoring EAP_SIM attribute %N",
390 sim_attribute_names
, attribute
);
395 /* build payload. If "include_id" is AT_END, AT_IDENTITY is ommited */
396 *out
= build_payload(this, identifier
, SIM_START
,
397 AT_SELECTED_VERSION
, this->version
,
398 AT_NONCE_MT
, this->nonce
,
399 include_id
, this->peer
->get_encoding(this->peer
),
405 * process an EAP-SIM/Request/Challenge message
407 static status_t
process_challenge(private_eap_sim_t
*this, eap_payload_t
*in
,
410 chunk_t message
, data
, tmp
, kcs
, kc
, sreses
, sres
, mk
;
411 sim_attribute_t attribute
;
413 chunk_t mac
= chunk_empty
, rands
= chunk_empty
;
418 identifier
= in
->get_identifier(in
);
419 message
= in
->get_data(in
);
420 read_header(&message
);
422 while ((attribute
= read_attribute(&message
, &data
)) != AT_END
)
428 rands
= chunk_skip(data
, 2);
433 /* backup MAC, zero it inline for later verification */
434 mac
= chunk_clonea(chunk_skip(data
, 2));
435 memset(data
.ptr
, 0, data
.len
);
439 DBG1(DBG_IKE
, "ignoring EAP_SIM attribute %N",
440 sim_attribute_names
, attribute
);
445 /* excepting two or three RAND, each 16 bytes. We require two valid
446 * and different (!) RANDs */
447 if ((rands
.len
!= 2 * RAND_LEN
&& rands
.len
!= 3 * RAND_LEN
) ||
448 memeq(rands
.ptr
, rands
.ptr
+ RAND_LEN
, RAND_LEN
))
450 DBG1(DBG_IKE
, "no valid AT_RAND received");
451 *out
= build_payload(this, identifier
, SIM_CLIENT_ERROR
,
452 AT_CLIENT_ERROR_CODE
, client_error_insufficient
,
456 if (mac
.len
!= MAC_LEN
)
458 DBG1(DBG_IKE
, "no valid AT_MAC received");
459 *out
= build_payload(this, identifier
, SIM_CLIENT_ERROR
,
460 AT_CLIENT_ERROR_CODE
, client_error_general
,
465 /* get two or three KCs/SRESes from SIM using RANDs */
466 kcs
= kc
= chunk_alloca(rands
.len
/ 2);
467 sreses
= sres
= chunk_alloca(rands
.len
/ 4);
468 while (rands
.len
> 0)
470 int kc_len
= kc
.len
, sres_len
= sres
.len
;
472 if (this->alg(rands
.ptr
, RAND_LEN
, kc
.ptr
, &kc_len
, sres
.ptr
, &sres_len
))
474 DBG1(DBG_IKE
, "unable to get triplets from SIM");
475 *out
= build_payload(this, identifier
, SIM_CLIENT_ERROR
,
476 AT_CLIENT_ERROR_CODE
, client_error_general
,
480 kc
= chunk_skip(kc
, kc_len
);
481 sres
= chunk_skip(sres
, sres_len
);
482 rands
= chunk_skip(rands
, RAND_LEN
);
486 tmp
= chunk_cata("ccccc", this->peer
->get_encoding(this->peer
), kcs
,
487 this->nonce
, this->version_list
, this->version
);
488 hasher
= hasher_create(HASH_SHA1
);
489 mk
= chunk_alloca(hasher
->get_hash_size(hasher
));
490 hasher
->get_hash(hasher
, tmp
, mk
.ptr
);
491 hasher
->destroy(hasher
);
493 /* K_encr | K_auth | MSK | EMSK = prf() | prf() | prf() | prf()
494 * FIPS PRF has 320 bit block size, we need 160 byte for keys
495 * => run prf four times */
496 prf
= prf_create(PRF_FIPS_SHA1_160
);
497 prf
->set_key(prf
, mk
);
498 tmp
= chunk_alloca(prf
->get_block_size(prf
) * 4);
499 prf
->get_bytes(prf
, chunk_empty
, tmp
.ptr
);
500 prf
->get_bytes(prf
, chunk_empty
, tmp
.ptr
+ tmp
.len
/ 4 * 1);
501 prf
->get_bytes(prf
, chunk_empty
, tmp
.ptr
+ tmp
.len
/ 4 * 2);
502 prf
->get_bytes(prf
, chunk_empty
, tmp
.ptr
+ tmp
.len
/ 4 * 3);
504 chunk_free(&this->k_encr
);
505 chunk_free(&this->k_auth
);
506 chunk_free(&this->msk
);
507 chunk_free(&this->emsk
);
508 chunk_split(tmp
, "aaaa", KENCR_LEN
, &this->k_encr
, KAUTH_LEN
, &this->k_auth
,
509 MSK_LEN
, &this->msk
, EMSK_LEN
, &this->emsk
);
510 DBG3(DBG_IKE
, "MK %B", &mk
);
511 DBG3(DBG_IKE
, "K_encr %B", &this->k_encr
);
512 DBG3(DBG_IKE
, "K_auth %B", &this->k_auth
);
513 DBG3(DBG_IKE
, "MSK %B", &this->msk
);
514 DBG3(DBG_IKE
, "EMSK %B", &this->emsk
);
516 /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT" */
517 signer
= signer_create(AUTH_HMAC_SHA1_128
);
518 signer
->set_key(signer
, this->k_auth
);
519 tmp
= chunk_cata("cc", message
, this->nonce
);
520 DBG3(DBG_IKE
, "verifying AT_MAC signature of %B", &tmp
);
521 DBG3(DBG_IKE
, "using k_auth %B", &this->k_auth
);
522 if (!signer
->verify_signature(signer
, tmp
, mac
))
524 DBG1(DBG_IKE
, "MAC in AT_MAC attribute verification failed");
525 signer
->destroy(signer
);
526 chunk_free(&this->msk
);
527 *out
= build_payload(this, identifier
, SIM_CLIENT_ERROR
,
528 AT_CLIENT_ERROR_CODE
, client_error_general
,
532 signer
->destroy(signer
);
534 /* build response, AT_MAC is built over "EAP packet | n*SRES" */
535 *out
= build_payload(this, identifier
, SIM_CHALLENGE
,
542 * Implementation of eap_method_t.process for the peer
544 static status_t
process(private_eap_sim_t
*this,
545 eap_payload_t
*in
, eap_payload_t
**out
)
550 message
= in
->get_data(in
);
551 type
= read_header(&message
);
556 return process_start(this, in
, out
);
558 return process_challenge(this, in
, out
);
560 DBG1(DBG_IKE
, "unable to process EAP_SIM subtype %N",
561 sim_subtype_names
, type
);
562 *out
= build_payload(this, in
->get_identifier(in
), SIM_CLIENT_ERROR
,
563 AT_CLIENT_ERROR_CODE
, client_error_general
, AT_END
);
569 * Implementation of eap_method_t.initiate for the peer
571 static status_t
initiate(private_eap_sim_t
*this, eap_payload_t
**out
)
573 /* peer never initiates */
578 * Implementation of eap_method_t.get_type.
580 static eap_type_t
get_type(private_eap_sim_t
*this)
586 * Implementation of eap_method_t.get_msk.
588 static status_t
get_msk(private_eap_sim_t
*this, chunk_t
*msk
)
599 * Implementation of eap_method_t.is_mutual.
601 static bool is_mutual(private_eap_sim_t
*this)
607 * Implementation of eap_method_t.destroy.
609 static void destroy(private_eap_sim_t
*this)
611 dlclose(this->handle
);
612 chunk_free(&this->nonce
);
613 chunk_free(&this->version_list
);
614 chunk_free(&this->k_auth
);
615 chunk_free(&this->k_encr
);
616 chunk_free(&this->msk
);
617 chunk_free(&this->emsk
);
622 * Described in header.
624 eap_sim_t
*eap_create(eap_role_t role
,
625 identification_t
*server
, identification_t
*peer
)
627 private_eap_sim_t
*this;
628 randomizer_t
*randomizer
;
629 static char version
[] = {0x00,0x01};
631 if (role
!= EAP_PEER
)
635 this = malloc_thing(private_eap_sim_t
);
637 this->handle
= dlopen(SIM_READER_LIB
, RTLD_LAZY
);
638 if (this->handle
== NULL
)
640 DBG1(DBG_IKE
, "unable to open SIM reader '%s'", SIM_READER_LIB
);
644 this->alg
= dlsym(this->handle
, SIM_READER_ALG
);
645 if (this->alg
== NULL
)
647 DBG1(DBG_IKE
, "unable to open SIM reader function '%s' in '%s'",
648 SIM_READER_ALG
, SIM_READER_LIB
);
649 dlclose(this->handle
);
654 randomizer
= randomizer_create();
655 if (randomizer
->allocate_pseudo_random_bytes(randomizer
, NONCE_LEN
,
658 DBG1(DBG_IKE
, "unable to generate NONCE for EAP_SIM");
659 randomizer
->destroy(randomizer
);
663 randomizer
->destroy(randomizer
);
665 /* public functions */
666 this->public.eap_method_interface
.initiate
= (status_t(*)(eap_method_t
*,eap_payload_t
**))initiate
;
667 this->public.eap_method_interface
.process
= (status_t(*)(eap_method_t
*,eap_payload_t
*,eap_payload_t
**))process
;
668 this->public.eap_method_interface
.get_type
= (eap_type_t(*)(eap_method_t
*))get_type
;
669 this->public.eap_method_interface
.is_mutual
= (bool(*)(eap_method_t
*))is_mutual
;
670 this->public.eap_method_interface
.get_msk
= (status_t(*)(eap_method_t
*,chunk_t
*))get_msk
;
671 this->public.eap_method_interface
.destroy
= (void(*)(eap_method_t
*))destroy
;
675 this->version
.ptr
= version
;
676 this->version
.len
= sizeof(version
);
677 this->version_list
= chunk_empty
;
678 this->k_auth
= chunk_empty
;
679 this->k_encr
= chunk_empty
;
680 this->msk
= chunk_empty
;
681 this->emsk
= chunk_empty
;
683 return &this->public;