diff options
author | Martin Willi <martin@strongswan.org> | 2009-10-08 16:49:29 +0200 |
---|---|---|
committer | Martin Willi <martin@strongswan.org> | 2009-10-09 13:02:20 +0200 |
commit | 53a16b72ab569b50dbd3144f41e77e3b2e6e29cf (patch) | |
tree | 7a238e66c1a98290a7d65b847461d97ea4cde055 | |
parent | 0030880c6b83498def8268225bf6a5e9953e9de0 (diff) | |
download | strongswan-53a16b72ab569b50dbd3144f41e77e3b2e6e29cf.tar.bz2 strongswan-53a16b72ab569b50dbd3144f41e77e3b2e6e29cf.tar.xz |
Separated 3gpp2 USIM card and provider functionality
-rw-r--r-- | src/charon/plugins/eap_aka/eap_aka.c | 853 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka/eap_aka.h | 25 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_card.c | 114 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_functions.c | 72 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_functions.h | 53 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_provider.c | 131 | ||||
-rw-r--r-- | src/charon/sa/authenticators/eap/usim_manager.h | 32 |
7 files changed, 435 insertions, 845 deletions
diff --git a/src/charon/plugins/eap_aka/eap_aka.c b/src/charon/plugins/eap_aka/eap_aka.c index 1cbe472d8..f7a1e2d91 100644 --- a/src/charon/plugins/eap_aka/eap_aka.c +++ b/src/charon/plugins/eap_aka/eap_aka.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Martin Willi + * Copyright (C) 2006-2009 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -44,36 +44,11 @@ #include <library.h> #include <crypto/hashers/hasher.h> -/* Use test vectors specified in S.S0055 -#define TEST_VECTORS */ - -#define RAND_LENGTH 16 -#define RES_LENGTH 16 -#define SQN_LENGTH 6 -#define K_LENGTH 16 -#define MAC_LENGTH 8 -#define CK_LENGTH 16 -#define IK_LENGTH 16 -#define AK_LENGTH 6 -#define AMF_LENGTH 2 -#define FMK_LENGTH 4 -#define AUTN_LENGTH (SQN_LENGTH + AMF_LENGTH + MAC_LENGTH) -#define AUTS_LENGTH (SQN_LENGTH + MAC_LENGTH) -#define PAYLOAD_LENGTH 64 -#define MK_LENGTH 20 -#define MSK_LENGTH 64 -#define EMSK_LENGTH 64 -#define KAUTH_LENGTH 16 -#define KENCR_LENGTH 16 -#define AT_MAC_LENGTH 16 - -#define F1 0x42 -#define F1STAR 0x43 -#define F2 0x44 -#define F3 0x45 -#define F4 0x46 -#define F5 0x47 -#define F5STAR 0x48 +#define MK_LEN 20 +#define MSK_LEN 64 +#define KAUTH_LEN 16 +#define KENCR_LEN 16 +#define AT_MAC_LEN 16 typedef enum aka_subtype_t aka_subtype_t; typedef enum aka_attribute_t aka_attribute_t; @@ -187,11 +162,6 @@ struct private_eap_aka_t { eap_aka_t public; /** - * ID of the server - */ - identification_t *server; - - /** * ID of the peer */ identification_t *peer; @@ -207,49 +177,29 @@ struct private_eap_aka_t { signer_t *signer; /** - * pseudo random function used in EAP-aka + * pseudo random function used in EAP-AKA */ prf_t *prf; /** - * Special keyed SHA1 hasher used in EAP-AKA, implemented as PRF - */ - prf_t *keyed_prf; - - /** - * Key for EAP MAC - */ - chunk_t k_auth; - - /** - * Key for EAP encryption - */ - chunk_t k_encr; - - /** * MSK */ - chunk_t msk; + char msk[MSK_LEN]; /** - * Extendend MSK + * Has the MSK been calculated? */ - chunk_t emsk; + bool derived; /** - * Expected result from client XRES + * (Expected) Result (X)RES */ - chunk_t xres; + char res[AKA_RES_LEN]; /** - * Shared secret K from ipsec.conf (padded) + * random value RAND (used by server only) */ - chunk_t k; - - /** - * random value RAND generated by server - */ - chunk_t rand; + char rand[AKA_RAND_LEN]; }; /** @@ -280,421 +230,49 @@ struct aka_attribute_header_t { u_int8_t length; } __attribute__((__packed__)); -/** Family key, as proposed in S.S0055 */ -static chunk_t fmk = chunk_from_chars(0x41, 0x48, 0x41, 0x47); - -/** Authentication management field */ -static chunk_t amf = chunk_from_chars(0x00, 0x01); - /** AT_CLIENT_ERROR_CODE AKA attribute */ static chunk_t client_error_code = chunk_from_chars(0, 0); -/** previously used sqn by peer, next one must be greater */ -static u_int8_t peer_sqn_buf[6]; -static chunk_t peer_sqn = {peer_sqn_buf, sizeof(peer_sqn_buf)}; - -/** set SQN to the current time */ -static void update_sqn(u_int8_t *sqn, time_t offset) -{ - timeval_t time; - - time_monotonic(&time); - /* set sqb_sqn to an integer containing seconds followed by most - * significant useconds */ - time.tv_sec = htonl(time.tv_sec + offset); - /* usec's are never larger than 0x000f423f, so we shift the 12 first bits */ - time.tv_usec <<= 12; - time.tv_usec = htonl(time.tv_usec); - memcpy(sqn, &time.tv_sec, 4); - memcpy(sqn + 4, &time.tv_usec, 2); -} - -/** initialize peers SQN to the current system time at startup */ -static void __attribute__ ((constructor))init_sqn(void) -{ - update_sqn(peer_sqn_buf, 0); -} - -/** - * Binary represnation of the polynom T^160 + T^5 + T^3 + T^2 + 1 - */ -static u_int8_t g[] = { - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2d -}; - -/** - * Predefined random bits from the RAND Corporation book - */ -static u_int8_t a[] = { - 0x9d, 0xe9, 0xc9, 0xc8, 0xef, 0xd5, 0x78, 0x11, - 0x48, 0x23, 0x14, 0x01, 0x90, 0x1f, 0x2d, 0x49, - 0x3f, 0x4c, 0x63, 0x65 -}; - -/** - * Predefined random bits from the RAND Corporation book - */ -static u_int8_t b[] = { - 0x75, 0xef, 0xd1, 0x5c, 0x4b, 0x8f, 0x8f, 0x51, - 0x4e, 0xf3, 0xbc, 0xc3, 0x79, 0x4a, 0x76, 0x5e, - 0x7e, 0xec, 0x45, 0xe0 -}; - -/** - * Multiplicate two mpz_t with bits interpreted as polynoms. - */ -static void mpz_mul_poly(mpz_t r, mpz_t a, mpz_t b) -{ - mpz_t bm, rm; - int current = 0, shifted = 0, shift; - - mpz_init_set(bm, b); - mpz_init_set_ui(rm, 0); - /* scan through a, for each found bit: */ - while ((current = mpz_scan1(a, current)) != ULONG_MAX) - { - /* XOR shifted b into r */ - shift = current - shifted; - mpz_mul_2exp(bm, bm, shift); - shifted += shift; - mpz_xor(rm, rm, bm); - current++; - } - - mpz_swap(r, rm); - mpz_clear(rm); - mpz_clear(bm); -} - -/** - * Calculate the sum of a + b interpreted as polynoms. - */ -static void mpz_add_poly(mpz_t res, mpz_t a, mpz_t b) -{ - /* addition of polynominals is just the XOR */ - mpz_xor(res, a, b); -} - -/** - * Calculate the remainder of a/b interpreted as polynoms. - */ -static void mpz_mod_poly(mpz_t r, mpz_t a, mpz_t b) -{ - /* Example: - * a = 10001010 - * b = 00000101 - */ - int a_bit, b_bit, diff; - mpz_t bm, am; - - mpz_init_set(am, a); - mpz_init(bm); - - a_bit = mpz_sizeinbase(a, 2); - b_bit = mpz_sizeinbase(b, 2); - - /* don't do anything if b > a */ - if (a_bit >= b_bit) - { - /* shift b left to align up most signaficant "1" to a: - * a = 10001010 - * b = 10100000 - */ - mpz_mul_2exp(bm, b, a_bit - b_bit); - do - { - /* XOR b into a, this kills the most significant "1": - * a = 00101010 - */ - mpz_xor(am, am, bm); - /* find the next most significant "1" in a, and align up b: - * a = 00101010 - * b = 00101000 - */ - diff = a_bit - mpz_sizeinbase(am, 2); - mpz_div_2exp(bm, bm, diff); - a_bit -= diff; - } - while (b_bit <= mpz_sizeinbase(bm, 2)); - /* While b is not shifted to its original value */ - } - /* after another iteration: - * a = 00000010 - * which is the polynomial modulo - */ - - mpz_swap(r, am); - mpz_clear(am); - mpz_clear(bm); -} - -/** - * Step 4 of the various fx() functions: - * Polynomial whiten calculations - */ -static void step4(private_eap_aka_t *this, u_int8_t x[]) -{ - mpz_t xm, am, bm, gm; - - mpz_init(xm); - mpz_init(am); - mpz_init(bm); - mpz_init(gm); - - mpz_import(xm, HASH_SIZE_SHA1, 1, 1, 1, 0, x); - mpz_import(am, sizeof(a), 1, 1, 1, 0, a); - mpz_import(bm, sizeof(b), 1, 1, 1, 0, b); - mpz_import(gm, sizeof(g), 1, 1, 1, 0, g); - - mpz_mul_poly(xm, am, xm); - mpz_add_poly(xm, bm, xm); - mpz_mod_poly(xm, xm, gm); - - mpz_export(x, NULL, 1, HASH_SIZE_SHA1, 1, 0, xm); - - mpz_clear(xm); - mpz_clear(am); - mpz_clear(bm); - mpz_clear(gm); -} - -/** - * Step 3 of the various fx() functions: - * XOR the key into the SHA1 IV - */ -static void step3(private_eap_aka_t *this, - chunk_t k, chunk_t payload, u_int8_t h[]) -{ - u_int8_t buf[64]; - - if (payload.len < sizeof(buf)) - { - /* pad c with zeros */ - memset(buf, 0, sizeof(buf)); - memcpy(buf, payload.ptr, payload.len); - payload.ptr = buf; - payload.len = sizeof(buf); - } - else - { - /* not more than 512 bits can be G()-ed */ - payload.len = sizeof(buf); - } - - /* use the keyed hasher to build the hash */ - this->keyed_prf->set_key(this->keyed_prf, k); - this->keyed_prf->get_bytes(this->keyed_prf, payload, h); -} - -/** - * Calculation function for f2(), f3(), f4() - */ -static void fx(private_eap_aka_t *this, - u_int8_t f, chunk_t k, chunk_t rand, u_int8_t out[]) -{ - chunk_t payload = chunk_alloca(PAYLOAD_LENGTH); - u_int8_t h[HASH_SIZE_SHA1]; - u_int8_t i; - - for (i = 0; i < 2; i++) - { - memset(payload.ptr, 0x5c, payload.len); - payload.ptr[11] ^= f; - memxor(payload.ptr + 12, fmk.ptr, fmk.len); - memxor(payload.ptr + 24, rand.ptr, rand.len); - - payload.ptr[3] ^= i; - payload.ptr[19] ^= i; - payload.ptr[35] ^= i; - payload.ptr[51] ^= i; - - step3(this, k, payload, h); - step4(this, h); - memcpy(out + i * 8, h, 8); - } -} - -/** - * Calculation function of f1() and f1star() - */ -static void f1x(private_eap_aka_t *this, - u_int8_t f, chunk_t k, chunk_t rand, chunk_t sqn, - chunk_t amf, u_int8_t mac[]) -{ - /* generate MAC = f1(FMK, SQN, RAND, AMF) - * K is loaded into hashers IV; FMK, RAND, SQN, AMF are XORed in a 512-bit - * payload which gets hashed - */ - chunk_t payload = chunk_alloca(PAYLOAD_LENGTH); - u_int8_t h[HASH_SIZE_SHA1]; - - memset(payload.ptr, 0x5c, PAYLOAD_LENGTH); - payload.ptr[11] ^= f; - memxor(payload.ptr + 12, fmk.ptr, fmk.len); - memxor(payload.ptr + 16, rand.ptr, rand.len); - memxor(payload.ptr + 34, sqn.ptr, sqn.len); - memxor(payload.ptr + 42, amf.ptr, amf.len); - - step3(this, k, payload, h); - step4(this, h); - memcpy(mac, h, MAC_LENGTH); -} - -/** - * Calculation function of f5() and f5star() - */ -static void f5x(private_eap_aka_t *this, - u_int8_t f, chunk_t k, chunk_t rand, u_int8_t ak[]) -{ - chunk_t payload = chunk_alloca(PAYLOAD_LENGTH); - u_int8_t h[HASH_SIZE_SHA1]; - - memset(payload.ptr, 0x5c, payload.len); - payload.ptr[11] ^= f; - memxor(payload.ptr + 12, fmk.ptr, fmk.len); - memxor(payload.ptr + 16, rand.ptr, rand.len); - - step3(this, k, payload, h); - step4(this, h); - memcpy(ak, h, AK_LENGTH); -} - -/** - * Calculate the MAC from a RAND, SQN, AMF value using K - */ -static void f1(private_eap_aka_t *this, chunk_t k, chunk_t rand, chunk_t sqn, - chunk_t amf, u_int8_t mac[]) -{ - f1x(this, F1, k, rand, sqn, amf, mac); - DBG3(DBG_IKE, "MAC %b", mac, MAC_LENGTH); -} - -/** - * Calculate the MACS from a RAND, SQN, AMF value using K - */ -static void f1star(private_eap_aka_t *this, chunk_t k, chunk_t rand, - chunk_t sqn, chunk_t amf, u_int8_t macs[]) -{ - f1x(this, F1STAR, k, rand, sqn, amf, macs); - DBG3(DBG_IKE, "MACS %b", macs, MAC_LENGTH); -} - -/** - * Calculate RES from RAND using K - */ -static void f2(private_eap_aka_t *this, chunk_t k, chunk_t rand, u_int8_t res[]) -{ - fx(this, F2, k, rand, res); - DBG3(DBG_IKE, "RES %b", res, RES_LENGTH); -} - -/** - * Calculate CK from RAND using K - */ -static void f3(private_eap_aka_t *this, chunk_t k, chunk_t rand, u_int8_t ck[]) -{ - fx(this, F3, k, rand, ck); - DBG3(DBG_IKE, "CK %b", ck, CK_LENGTH); -} - -/** - * Calculate IK from RAND using K - */ -static void f4(private_eap_aka_t *this, chunk_t k, chunk_t rand, u_int8_t ik[]) -{ - fx(this, F4, k, rand, ik); - DBG3(DBG_IKE, "IK %b", ik, IK_LENGTH); -} - -/** - * Calculate AK from a RAND using K - */ -static void f5(private_eap_aka_t *this, chunk_t k, chunk_t rand, u_int8_t ak[]) -{ - f5x(this, F5, k, rand, ak); - DBG3(DBG_IKE, "AK %b", ak, AK_LENGTH); -} - -/** - * Calculate AKS from a RAND using K - */ -static void f5star(private_eap_aka_t *this, chunk_t k, chunk_t rand, u_int8_t aks[]) -{ - f5x(this, F5STAR, k, rand, aks); - DBG3(DBG_IKE, "AKS %b", aks, AK_LENGTH); -} - /** * derive the keys needed for EAP_AKA */ -static bool derive_keys(private_eap_aka_t *this, identification_t *id) +static void derive_keys(private_eap_aka_t *this, identification_t *id, + chunk_t ck, chunk_t ik) { - chunk_t ck, ik, mk, identity, tmp; - - ck = chunk_alloca(CK_LENGTH); - ik = chunk_alloca(IK_LENGTH); - mk = chunk_alloca(MK_LENGTH); - identity = id->get_encoding(id); + char mk[MK_LEN]; + chunk_t tmp, k_auth; /* MK = SHA1( Identity | IK | CK ) */ - f3(this, this->k, this->rand, ck.ptr); - f4(this, this->k, this->rand, ik.ptr); - DBG3(DBG_IKE, "Identity %B", &identity); - tmp = chunk_cata("ccc", identity, ik, ck); - DBG3(DBG_IKE, "Identity|IK|CK %B", &tmp); - this->sha1->get_hash(this->sha1, tmp, mk.ptr); + DBG3(DBG_IKE, "Identity|IK|CK => %#B|%#B|%#B", &id->get_encoding, &ik, &ck); + this->sha1->get_hash(this->sha1, id->get_encoding(id), NULL); + this->sha1->get_hash(this->sha1, ik, NULL); + this->sha1->get_hash(this->sha1, ck, mk); + DBG3(DBG_IKE, "MK %b", mk, sizeof(mk)); /* K_encr | K_auth | MSK | EMSK = prf(0) | prf(0) * FIPS PRF has 320 bit block size, we need 160 byte for keys * => run prf four times */ - this->prf->set_key(this->prf, mk); + this->prf->set_key(this->prf, chunk_create(mk, sizeof(mk))); tmp = chunk_alloca(this->prf->get_block_size(this->prf) * 4); this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr); this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr + tmp.len / 4 * 1); this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr + tmp.len / 4 * 2); this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr + tmp.len / 4 * 3); - chunk_free(&this->k_encr); - chunk_free(&this->k_auth); - chunk_free(&this->msk); - chunk_free(&this->emsk); - chunk_split(tmp, "aaaa", 16, &this->k_encr, 16, &this->k_auth, - 64, &this->msk, 64, &this->emsk); - DBG3(DBG_IKE, "MK %B", &mk); - DBG3(DBG_IKE, "PRF res %B", &tmp); - DBG3(DBG_IKE, "K_encr %B", &this->k_encr); - DBG3(DBG_IKE, "K_auth %B", &this->k_auth); - DBG3(DBG_IKE, "MSK %B", &this->msk); - DBG3(DBG_IKE, "EMSK %B", &this->emsk); - return TRUE; -} -/* - * Get a shared key from ipsec.secrets. - * We use the standard keys as used in preshared key authentication. As - * these keys have an undefined length, we: - * - strip them if they are longer - * - fill them up with '\0' if they are shorter - */ -static status_t load_key(identification_t *me, identification_t *other, chunk_t *k) -{ - shared_key_t *shared; - chunk_t key; + /* skip K_encr, not required */ + tmp = chunk_skip(tmp, KENCR_LEN); + k_auth = chunk_create(tmp.ptr, KAUTH_LEN); + tmp = chunk_skip(tmp, KAUTH_LEN); + memcpy(this->msk, tmp.ptr, MSK_LEN); + /* ignore EMSK, not required */ - shared = charon->credentials->get_shared(charon->credentials, SHARED_EAP, - me, other); - if (shared == NULL) - { - return NOT_FOUND; - } - key = shared->get_key(shared); - chunk_free(k); - *k = chunk_alloc(K_LENGTH); - memset(k->ptr, '\0', k->len); - memcpy(k->ptr, key.ptr, min(key.len, k->len)); - shared->destroy(shared); - return SUCCESS; + this->signer->set_key(this->signer, k_auth); + + DBG3(DBG_IKE, "PRF res %B", &tmp); + DBG3(DBG_IKE, "K_auth %B", &k_auth); + DBG3(DBG_IKE, "MSK %b", this->msk, sizeof(this->msk)); + + this->derived = TRUE; } /** @@ -813,8 +391,8 @@ static eap_payload_t *build_aka_payload(private_eap_aka_t *this, eap_code_t code memset(pos.ptr, 0, 2); pos = chunk_skip(pos, 2); mac_pos = pos.ptr; - memset(mac_pos, 0, AT_MAC_LENGTH); - pos = chunk_skip(pos, AT_MAC_LENGTH); + memset(mac_pos, 0, AT_MAC_LEN); + pos = chunk_skip(pos, AT_MAC_LEN); break; } case AT_IDENTITY: @@ -856,11 +434,9 @@ static eap_payload_t *build_aka_payload(private_eap_aka_t *this, eap_code_t code /* create MAC if AT_MAC attribte was included */ if (mac_pos) { - this->signer->set_key(this->signer, this->k_auth); DBG3(DBG_IKE, "AT_MAC signature of %B", &message); - DBG3(DBG_IKE, "using key %B", &this->k_auth); this->signer->get_signature(this->signer, message, mac_pos); - DBG3(DBG_IKE, "is %b", mac_pos, AT_MAC_LENGTH); + DBG3(DBG_IKE, "is %b", mac_pos, AT_MAC_LEN); } /* payload constructor takes data with some bytes skipped */ @@ -902,86 +478,48 @@ static eap_payload_t *build_non_skippable_error(private_eap_aka_t *this, */ static u_char get_identifier() { - u_char id; + while (TRUE) + { + u_char id = random(); - do { - id = random(); - } while (!id); - return id; + if (id) + { + return id; + } + } } /** - * Initiate a AKA-Challenge using SQN + * Implementation of eap_method_t.initiate for an EAP_AKA server */ -static status_t server_initiate_challenge(private_eap_aka_t *this, chunk_t sqn, - eap_payload_t **out) +static status_t server_initiate(private_eap_aka_t *this, eap_payload_t **out) { - rng_t *rng; - chunk_t mac, ak, autn; + enumerator_t *enumerator; + usim_provider_t *provider; + char ck[AKA_CK_LEN], ik[AKA_IK_LEN], autn[AKA_AUTN_LEN]; + bool found = FALSE; - mac = chunk_alloca(MAC_LENGTH); - ak = chunk_alloca(AK_LENGTH); - chunk_free(&this->rand); - chunk_free(&this->xres); - - /* generate RAND: - * we use a registered RNG, not f0() proposed in S.S0055 - */ - rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); - if (!rng) + enumerator = charon->usim->create_provider_enumerator(charon->usim); + while (enumerator->enumerate(enumerator, &provider)) { - DBG1(DBG_IKE, "generating RAND for EAP-AKA authentication failed"); - return FAILED; + if (provider->get_quintuplet(provider, this->peer, this->rand, + this->res, ck, ik, autn)) + { + found = TRUE; + break; + } } - rng->allocate_bytes(rng, RAND_LENGTH, &this->rand); - rng->destroy(rng); - -# ifdef TEST_VECTORS - /* Test vector for RAND */ - u_int8_t test_rand[] = { - 0x4b,0x05,0x2b,0x20,0xe2,0xa0,0x6c,0x8f, - 0xf7,0x00,0xda,0x51,0x2b,0x4e,0x11,0x1e, - }; - memcpy(this->rand.ptr, test_rand, this->rand.len); -# endif /* TEST_VECTORS */ - - /* Get the shared key K: */ - if (load_key(this->server, this->peer, &this->k) != SUCCESS) + enumerator->destroy(enumerator); + if (!found) { - DBG1(DBG_IKE, "no shared key found for IDs '%Y' - '%Y' to authenticate " - "with EAP-AKA", this->server, this->peer); + DBG1(DBG_IKE, "no AKA provider found with quintuplets for %Y", + this->peer); return FAILED; } -# ifdef TEST_VECTORS - /* Test vector for K */ - u_int8_t test_k[] = { - 0xad,0x1b,0x5a,0x15,0x9b,0xe8,0x6b,0x2c, - 0xa6,0x6c,0x7a,0xe4,0x0b,0xba,0x9b,0x9d, - }; - memcpy(this->k.ptr, test_k, this->k.len); -# endif /* TEST_VECTORS */ - - /* generate MAC */ - f1(this, this->k, this->rand, sqn, amf, mac.ptr); + derive_keys(this, this->peer, chunk_create(ck, sizeof(ck)), + chunk_create(ik, sizeof(ik))); - /* generate AK */ - f5(this, this->k, this->rand, ak.ptr); - - /* precalculate XRES as expected from client */ - this->xres = chunk_alloc(RES_LENGTH); - f2(this, this->k, this->rand, this->xres.ptr); - - /* calculate AUTN = (SQN xor AK) || AMF || MAC */ - autn = chunk_cata("ccc", sqn, amf, mac); - memxor(autn.ptr, ak.ptr, ak.len); - DBG3(DBG_IKE, "AUTN %B", &autn); - - - /* derive K_encr, K_auth, MSK, EMSK */ - derive_keys(this, this->peer); - - /* build payload */ *out = build_aka_payload(this, EAP_REQUEST, get_identifier(), AKA_CHALLENGE, AT_RAND, this->rand, AT_AUTN, autn, AT_MAC, chunk_empty, AT_END); @@ -989,37 +527,21 @@ static status_t server_initiate_challenge(private_eap_aka_t *this, chunk_t sqn, } /** - * Implementation of eap_method_t.initiate for an EAP_AKA server + * Process synchronization request from peer */ -static status_t server_initiate(private_eap_aka_t *this, eap_payload_t **out) -{ - chunk_t sqn = chunk_alloca(SQN_LENGTH); - - /* we use an offset of 3 minutes to tolerate clock inaccuracy - * without the need to synchronize sequence numbers */ - update_sqn(sqn.ptr, 180); - -# ifdef TEST_VECTORS - /* Test vector for SQN */ - u_int8_t test_sqn[] = {0x00,0x00,0x00,0x00,0x00,0x01}; - memcpy(sqn.ptr, test_sqn, sqn.len); -# endif /* TEST_VECTORS */ - - return server_initiate_challenge(this, sqn, out); -} - static status_t server_process_synchronize(private_eap_aka_t *this, - eap_payload_t *in, eap_payload_t **out) + eap_payload_t *in, eap_payload_t **out) { - chunk_t attr, auts = chunk_empty, pos, message, macs, xmacs, sqn, aks, amf; + chunk_t attr, message, pos, auts = chunk_empty; aka_attribute_t attribute; - u_int i; + enumerator_t *enumerator; + usim_provider_t *provider; + bool found = FALSE; message = in->get_data(in); pos = message; read_header(&pos); - /* iterate over attributes */ while (TRUE) { attribute = read_attribute(&pos, &attr); @@ -1042,40 +564,28 @@ static status_t server_process_synchronize(private_eap_aka_t *this, break; } - if (auts.len != AUTS_LENGTH) + if (auts.len != AKA_AUTS_LEN) { - DBG1(DBG_IKE, "synchronization request didn't contain useable AUTS"); + DBG1(DBG_IKE, "synchronization request didn't contain usable AUTS"); return FAILED; } - chunk_split(auts, "mm", SQN_LENGTH, &sqn, MAC_LENGTH, &macs); - aks = chunk_alloca(AK_LENGTH); - f5star(this, this->k, this->rand, aks.ptr); - /* decrypt serial number by XORing AKS */ - memxor(sqn.ptr, aks.ptr, aks.len); - - /* verify MACS */ - xmacs = chunk_alloca(MAC_LENGTH); - amf = chunk_alloca(AMF_LENGTH); - /* an AMF of zero is used for MACS calculation */ - memset(amf.ptr, 0, amf.len); - f1star(this, this->k, this->rand, sqn, amf, xmacs.ptr); - if (!chunk_equals(macs, xmacs)) + enumerator = charon->usim->create_provider_enumerator(charon->usim); + while (enumerator->enumerate(enumerator, &provider)) { - DBG1(DBG_IKE, "received MACS does not match XMACS"); - DBG3(DBG_IKE, "MACS %B XMACS %B", &macs, &xmacs); - return FAILED; - } - - /* retry the challenge with the received SQN + 1*/ - for (i = SQN_LENGTH - 1; i >= 0; i--) - { - if (++sqn.ptr[i] != 0) + if (provider->resync(provider, this->peer, this->rand, auts.ptr)) { + found = TRUE; break; } } - return server_initiate_challenge(this, sqn, out); + enumerator->destroy(enumerator); + + if (!found) + { + return FAILED; + } + return server_initiate(this, out); } /** @@ -1091,7 +601,6 @@ static status_t server_process_challenge(private_eap_aka_t *this, eap_payload_t pos = message; read_header(&pos); - /* iterate over attributes */ while (TRUE) { attribute = read_attribute(&pos, &attr); @@ -1101,10 +610,10 @@ static status_t server_process_challenge(private_eap_aka_t *this, eap_payload_t break; case AT_RES: res = attr; - if (attr.len == 2 + RES_LENGTH) + if (attr.len == 2 + AKA_RES_LEN) { memcpy(&len, attr.ptr, 2); - if (ntohs(len) == RES_LENGTH * 8) + if (ntohs(len) == AKA_RES_LEN * 8) { res = chunk_skip(attr, 2); } @@ -1129,22 +638,18 @@ static status_t server_process_challenge(private_eap_aka_t *this, eap_payload_t } /* verify EAP message MAC AT_MAC */ + DBG3(DBG_IKE, "verifying AT_MAC signature of %B", &message); + if (!this->signer->verify_signature(this->signer, message, at_mac)) { - this->signer->set_key(this->signer, this->k_auth); - DBG3(DBG_IKE, "verifying AT_MAC signature of %B", &message); - DBG3(DBG_IKE, "using key %B", &this->k_auth); - if (!this->signer->verify_signature(this->signer, message, at_mac)) - { - DBG1(DBG_IKE, "MAC in AT_MAC attribute verification failed"); - return FAILED; - } + DBG1(DBG_IKE, "MAC in AT_MAC attribute verification failed"); + return FAILED; } /* compare received RES against stored precalculated XRES */ - if (!chunk_equals(res, this->xres)) + if (!chunk_equals(res, chunk_create(this->res, sizeof(this->res)))) { DBG1(DBG_IKE, "received RES does not match XRES"); - DBG3(DBG_IKE, "RES %Bb XRES %B", &res, &this->xres); + DBG3(DBG_IKE, "RES %B XRES %b", &res, this->res, sizeof(this->res)); return FAILED; } return SUCCESS; @@ -1196,16 +701,14 @@ static status_t server_process(private_eap_aka_t *this, static status_t peer_process_challenge(private_eap_aka_t *this, eap_payload_t *in, eap_payload_t **out) { - chunk_t attr = chunk_empty; - chunk_t autn = chunk_empty, at_mac = chunk_empty; - chunk_t ak, sqn, sqn_ak, mac, xmac, res, amf, message, pos; + chunk_t autn = chunk_empty, rand = chunk_empty, at_mac = chunk_empty; + chunk_t message, pos, attr = chunk_empty; aka_attribute_t attribute; u_int8_t identifier; - - ak = chunk_alloca(AK_LENGTH); - xmac = chunk_alloca(MAC_LENGTH); - res = chunk_alloca(RES_LENGTH); - chunk_free(&this->rand); + enumerator_t *enumerator; + usim_card_t *card; + u_char res[AKA_RES_LEN], ck[AKA_CK_LEN], ik[AKA_IK_LEN], auts[AKA_AUTS_LEN]; + status_t status = NOT_FOUND; message = in->get_data(in); pos = message; @@ -1214,7 +717,6 @@ static status_t peer_process_challenge(private_eap_aka_t *this, DBG3(DBG_IKE, "reading attributes from %B", &pos); - /* iterate over attributes */ while (TRUE) { attribute = read_attribute(&pos, &attr); @@ -1223,7 +725,7 @@ static status_t peer_process_challenge(private_eap_aka_t *this, case AT_END: break; case AT_RAND: - this->rand = chunk_clone(chunk_skip(attr, 2)); + rand = chunk_skip(attr, 2); continue; case AT_AUTN: autn = chunk_skip(attr, 2); @@ -1245,102 +747,52 @@ static status_t peer_process_challenge(private_eap_aka_t *this, break; } - if (this->rand.len != RAND_LENGTH || autn.len != AUTN_LENGTH) + if (rand.len != AKA_RAND_LEN || autn.len != AKA_AUTN_LEN) { /* required attributes wrong/not found, abort */ *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_code, AT_END); + AT_CLIENT_ERROR_CODE, client_error_code, AT_END); DBG1(DBG_IKE, "could not find valid RAND/AUTN attribute, sending %N %d", aka_attribute_names, AT_CLIENT_ERROR_CODE, 0); return NEED_MORE; } - DBG3(DBG_IKE, "using autn %B", &autn); - /* split up AUTN = SQN xor AK | AMF | MAC */ - chunk_split(autn, "mmm", SQN_LENGTH, &sqn_ak, AMF_LENGTH, &amf, MAC_LENGTH, &mac); - - /* Get the shared key K: */ - chunk_free(&this->k); - if (load_key(this->peer, this->server, &this->k) != SUCCESS) + enumerator = charon->usim->create_card_enumerator(charon->usim); + while (enumerator->enumerate(enumerator, &card)) { - *out = build_aka_payload(this, EAP_RESPONSE, identifier, - AKA_AUTHENTICATION_REJECT, AT_END); - DBG3(DBG_IKE, "no shared key found for IDs '%Y' - '%Y' to authenticate " - "with EAP-AKA, sending %N", this->peer, this->server, - aka_subtype_names, AKA_AUTHENTICATION_REJECT); - return NEED_MORE; + status = card->get_quintuplet(card, this->peer, rand.ptr, autn.ptr, + ck, ik, res); + if (status != FAILED) + { /* try next on error */ + break; + } } - DBG3(DBG_IKE, "using K %B", &this->k); -# ifdef TEST_VECTORS - /* Test vector for K */ - u_int8_t test_k[] = { - 0xad,0x1b,0x5a,0x15,0x9b,0xe8,0x6b,0x2c, - 0xa6,0x6c,0x7a,0xe4,0x0b,0xba,0x9b,0x9d, - }; - memcpy(this->k.ptr, test_k, this->k.len); -# endif /* TEST_VECTORS */ - - /* calculate anonymity key AK */ - f5(this, this->k, this->rand, ak.ptr); - DBG3(DBG_IKE, "using rand %B", &this->rand); - DBG3(DBG_IKE, "using ak %B", &ak); - /* XOR AK into SQN to decrypt it */ - - sqn = chunk_clonea(sqn_ak); - - DBG3(DBG_IKE, "using ak xor sqn %B", &sqn_ak); - memxor(sqn.ptr, ak.ptr, sqn.len); - DBG3(DBG_IKE, "using sqn %B", &sqn); - - /* calculate expected MAC and compare against received one */ - f1(this, this->k, this->rand, sqn, amf, xmac.ptr); - if (!chunk_equals(mac, xmac)) + enumerator->destroy(enumerator); + + if (status == INVALID_STATE && + card->resync(card, this->peer, rand.ptr, auts)) { - *out = build_aka_payload(this, EAP_RESPONSE, identifier, - AKA_AUTHENTICATION_REJECT, AT_END); - DBG1(DBG_IKE, "received MAC does not match XMAC, sending %N", - aka_subtype_names, AKA_AUTHENTICATION_REJECT); - DBG3(DBG_IKE, "MAC %B\nXMAC %B", &mac, &xmac); + *out = build_aka_payload(this, EAP_RESPONSE, + identifier, AKA_SYNCHRONIZATION_FAILURE, + AT_AUTS, chunk_create(auts, sizeof(auts)), AT_END); + DBG1(DBG_IKE, "received SQN invalid, sending %N", + aka_subtype_names, AKA_SYNCHRONIZATION_FAILURE); return NEED_MORE; } - -#if SEQ_CHECK - if (memcmp(peer_sqn.ptr, sqn.ptr, sqn.len) >= 0) + if (status != SUCCESS) { - /* sequence number invalid. send AUTS */ - chunk_t auts, macs, aks, amf; - - macs = chunk_alloca(MAC_LENGTH); - aks = chunk_alloca(AK_LENGTH); - amf = chunk_alloca(AMF_LENGTH); - - /* AMF is set to zero in AKA_SYNCHRONIZATION_FAILURE */ - memset(amf.ptr, 0, amf.len); - /* AKS = f5*(RAND) */ - f5star(this, this->k, this->rand, aks.ptr); - /* MACS = f1*(RAND) */ - f1star(this, this->k, this->rand, peer_sqn, amf, macs.ptr); - /* AUTS = SQN xor AKS | MACS */ - memxor(aks.ptr, peer_sqn.ptr, aks.len); - auts = chunk_cata("cc", aks, macs); - *out = build_aka_payload(this, EAP_RESPONSE, identifier, - AKA_SYNCHRONIZATION_FAILURE, - AT_AUTS, auts, AT_END); - DBG1(DBG_IKE, "received SQN invalid, sending %N", - aka_subtype_names, AKA_SYNCHRONIZATION_FAILURE); - DBG3(DBG_IKE, "received SQN %B\ncurrent SQN %B", &sqn, &peer_sqn); + AKA_AUTHENTICATION_REJECT, AT_END); + DBG1(DBG_IKE, "no USIM found with quintuplets for %Y, sending %N", + this->peer, aka_subtype_names, AKA_AUTHENTICATION_REJECT); return NEED_MORE; } -#endif /* SEQ_CHECK */ - /* derive K_encr, K_auth, MSK, EMSK */ - derive_keys(this, this->peer); + derive_keys(this, this->peer, chunk_create(ck, sizeof(ck)), + chunk_create(ik, sizeof(ik))); /* verify EAP message MAC AT_MAC */ DBG3(DBG_IKE, "verifying AT_MAC signature of %B", &message); - DBG3(DBG_IKE, "using key %B", &this->k_auth); - this->signer->set_key(this->signer, this->k_auth); if (!this->signer->verify_signature(this->signer, message, at_mac)) { *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR, @@ -1351,15 +803,9 @@ static status_t peer_process_challenge(private_eap_aka_t *this, return NEED_MORE; } - /* update stored SQN to the received one */ - memcpy(peer_sqn.ptr, sqn.ptr, sqn.len); - - /* calculate RES */ - f2(this, this->k, this->rand, res.ptr); - - /* build response */ *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CHALLENGE, - AT_RES, res, AT_MAC, chunk_empty, AT_END); + AT_RES, chunk_create(res, sizeof(res)), + AT_MAC, chunk_empty, AT_END); return NEED_MORE; } @@ -1378,7 +824,6 @@ static status_t peer_process_identity(private_eap_aka_t *this, DBG3(DBG_IKE, "reading attributes from %B", &pos); - /* iterate over attributes */ while (TRUE) { aka_attribute_t attribute = read_attribute(&pos, &attr); @@ -1503,7 +948,7 @@ static status_t peer_process(private_eap_aka_t *this, type = read_header(&message); identifier = in->get_identifier(in); - DBG3(DBG_IKE, "received EAP message %B", &message); + DBG3(DBG_IKE, "received EAP message %B", &message); switch (type) { @@ -1554,9 +999,9 @@ static eap_type_t get_type(private_eap_aka_t *this, u_int32_t *vendor) */ static status_t get_msk(private_eap_aka_t *this, chunk_t *msk) { - if (this->msk.ptr) + if (this->derived) { - *msk = this->msk; + *msk = chunk_create(this->msk, sizeof(this->msk)); return SUCCESS; } return FAILED; @@ -1575,27 +1020,17 @@ static bool is_mutual(private_eap_aka_t *this) */ static void destroy(private_eap_aka_t *this) { - this->server->destroy(this->server); this->peer->destroy(this->peer); DESTROY_IF(this->sha1); DESTROY_IF(this->signer); DESTROY_IF(this->prf); - DESTROY_IF(this->keyed_prf); - chunk_free(&this->k_encr); - chunk_free(&this->k_auth); - chunk_free(&this->msk); - chunk_free(&this->emsk); - chunk_free(&this->xres); - chunk_free(&this->k); - chunk_free(&this->rand); free(this); } /** * generic constructor used by client & server */ -static private_eap_aka_t *eap_aka_create_generic(identification_t *server, - identification_t *peer) +static private_eap_aka_t *eap_aka_create_generic(identification_t *peer) { private_eap_aka_t *this = malloc_thing(private_eap_aka_t); @@ -1606,29 +1041,15 @@ static private_eap_aka_t *eap_aka_create_generic(identification_t *server, this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk; this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy; - /* private data */ - this->server = server->clone(server); this->peer = peer->clone(peer); - this->k_encr = chunk_empty; - this->k_auth = chunk_empty; - this->msk = chunk_empty; - this->emsk = chunk_empty; - this->xres = chunk_empty; - this->k = chunk_empty; - this->rand = chunk_empty; + this->derived = FALSE; this->sha1 = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_SHA1_128); this->prf = lib->crypto->create_prf(lib->crypto, PRF_FIPS_SHA1_160); - this->keyed_prf = lib->crypto->create_prf(lib->crypto, PRF_KEYED_SHA1); - - if (!this->sha1 || !this->signer || !this->prf || !this->keyed_prf) + if (!this->sha1 || !this->signer || !this->prf) { DBG1(DBG_IKE, "unable to initiate EAP-AKA, FIPS-PRF/SHA1 not supported"); - DESTROY_IF(this->sha1); - DESTROY_IF(this->signer); - DESTROY_IF(this->prf); - DESTROY_IF(this->keyed_prf); destroy(this); return NULL; } @@ -1640,7 +1061,7 @@ static private_eap_aka_t *eap_aka_create_generic(identification_t *server, */ eap_aka_t *eap_aka_create_server(identification_t *server, identification_t *peer) { - private_eap_aka_t *this = eap_aka_create_generic(server, peer); + private_eap_aka_t *this = eap_aka_create_generic(peer); if (this) { @@ -1655,7 +1076,7 @@ eap_aka_t *eap_aka_create_server(identification_t *server, identification_t *pee */ eap_aka_t *eap_aka_create_peer(identification_t *server, identification_t *peer) { - private_eap_aka_t *this = eap_aka_create_generic(server, peer); + private_eap_aka_t *this = eap_aka_create_generic(peer); if (this) { diff --git a/src/charon/plugins/eap_aka/eap_aka.h b/src/charon/plugins/eap_aka/eap_aka.h index 7686802cf..e12270c9f 100644 --- a/src/charon/plugins/eap_aka/eap_aka.h +++ b/src/charon/plugins/eap_aka/eap_aka.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Martin Willi + * Copyright (C) 2008-2009 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -25,32 +25,11 @@ typedef struct eap_aka_t eap_aka_t; #include <sa/authenticators/eap/eap_method.h> -/** check SEQ values as client for validity, disabled by default */ -#ifndef SEQ_CHECK -# define SEQ_CHECK 0 -#endif - /** * Implementation of the eap_method_t interface using EAP-AKA. * * EAP-AKA uses 3rd generation mobile phone standard authentication - * mechanism for authentication. It is a mutual authentication - * mechanism which establishs a shared key and therefore supports EAP_ONLY - * authentication. This implementation follows the standard of the - * 3GPP2 (S.S0055) and not the one of 3GGP. - * The shared key used for authentication is from ipsec.secrets. The - * peers ID is used to query it. - * The AKA mechanism uses sequence numbers to detect replay attacks. The - * peer stores the sequence number normally in a USIM and accepts - * incremental sequence numbers (incremental for lifetime of the USIM). To - * prevent a complex sequence number management, this implementation uses - * a sequence number derived from time. It is initialized to the startup - * time of the daemon. As long as the (UTC) time of the system is not - * turned back while the daemon is not running, this method is secure. - * To enable time based SEQs, define SEQ_CHECK as 1. Default is to accept - * any SEQ numbers. This allows an attacker to do replay attacks. But since - * the server has proven his identity via IKE, such an attack is only - * possible between server and AAA (if any). + * mechanism for authentication, as defined RFC4187. */ struct eap_aka_t { diff --git a/src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_card.c b/src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_card.c index 4d584f29a..aeaa683db 100644 --- a/src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_card.c +++ b/src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_card.c @@ -30,41 +30,108 @@ struct private_eap_aka_3gpp2_card_t { eap_aka_3gpp2_card_t public; /** - * IMSI, is ID_ANY for this software implementation + * AKA functions */ - identification_t *imsi; + eap_aka_3gpp2_functions_t *f; /** - * AKA functions + * do sequence number checking? */ - eap_aka_3gpp2_functions_t *f; + bool seq_check; + + /** + * SQN stored in this pseudo-USIM + */ + char sqn[AKA_SQN_LEN]; }; /** - * Implementation of usim_card_t.get_imsi + * Functions from eap_aka_3gpp2_provider.c */ -static identification_t* get_imsi(private_eap_aka_3gpp2_card_t *this) -{ - return this->imsi; -} +bool eap_aka_3gpp2_get_k(identification_t *id, char k[AKA_K_LEN]); +void eap_aka_3gpp2_get_sqn(char sqn[AKA_SQN_LEN], int offset); /** * Implementation of usim_card_t.get_quintuplet */ static status_t get_quintuplet(private_eap_aka_3gpp2_card_t *this, - char rand[16], char autn[16], - char ck[16], char ik[16], char res[16]) + identification_t *imsi, char rand[AKA_RAND_LEN], + char autn[AKA_AUTN_LEN], char ck[AKA_CK_LEN], + char ik[AKA_IK_LEN], char res[AKA_RES_LEN]) { - return FAILED; + char *amf, *mac; + char k[AKA_K_LEN], ak[AKA_AK_LEN], sqn[AKA_SQN_LEN], xmac[AKA_MAC_LEN]; + + if (!eap_aka_3gpp2_get_k(imsi, k)) + { + DBG1(DBG_IKE, "no EAP key found for %Y to authenticate with AKA", imsi); + return FALSE; + } + + /* AUTN = SQN xor AK | AMF | MAC */ + DBG3(DBG_IKE, "received autn %b", autn, sizeof(autn)); + DBG3(DBG_IKE, "using K %b", k, sizeof(k)); + DBG3(DBG_IKE, "using rand %b", rand, sizeof(rand)); + memcpy(sqn, autn, sizeof(sqn)); + amf = autn + sizeof(sqn); + mac = autn + sizeof(sqn) + AKA_AMF_LEN; + + /* XOR anonymity key AK into SQN to decrypt it */ + this->f->f5(this->f, k, rand, ak); + DBG3(DBG_IKE, "using ak %b", ak, sizeof(ak)); + memxor(sqn, ak, sizeof(sqn)); + DBG3(DBG_IKE, "using sqn %b", sqn, sizeof(sqn)); + + /* calculate expected MAC and compare against received one */ + this->f->f1(this->f, k, rand, sqn, amf, xmac); + if (!memeq(mac, xmac, sizeof(xmac))) + { + DBG1(DBG_IKE, "received MAC does not match XMAC"); + DBG3(DBG_IKE, "MAC %b\nXMAC %b", mac, AKA_MAC_LEN, xmac, AKA_MAC_LEN); + return FAILED; + } + + if (this->seq_check && memcmp(this->sqn, sqn, sizeof(sqn)) >= 0) + { + DBG3(DBG_IKE, "received SQN %b\ncurrent SQN %b", + sqn, sizeof(sqn), this->sqn, sizeof(this->sqn)); + return INVALID_STATE; + } + + /* update stored SQN to the received one */ + memcpy(this->sqn, sqn, sizeof(sqn)); + + /* calculate RES */ + this->f->f2(this->f, k, rand, res); + DBG3(DBG_IKE, "calculated rand %b", res, sizeof(res)); + + return SUCCESS; } /** * Implementation of usim_card_t.resync */ -static bool resync(private_eap_aka_3gpp2_card_t *this, - char rand[16], char auts[16]) +static bool resync(private_eap_aka_3gpp2_card_t *this, identification_t *imsi, + char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]) { - return FALSE; + char amf[AKA_AMF_LEN], k[AKA_K_LEN], aks[AKA_AK_LEN], macs[AKA_MAC_LEN]; + + if (!eap_aka_3gpp2_get_k(imsi, k)) + { + DBG1(DBG_IKE, "no EAP key found for %Y to resync AKA", imsi); + return FALSE; + } + + /* AMF is set to zero in resync */ + memset(amf, 0, sizeof(amf)); + this->f->f5star(this->f, k, rand, aks); + this->f->f1star(this->f, k, rand, this->sqn, amf, macs); + /* AUTS = SQN xor AKS | MACS */ + memcpy(auts, this->sqn, sizeof(this->sqn)); + memxor(auts, aks, sizeof(aks)); + memcpy(auts + sizeof(aks), macs, sizeof(macs)); + + return TRUE; } /** @@ -72,7 +139,6 @@ static bool resync(private_eap_aka_3gpp2_card_t *this, */ static void destroy(private_eap_aka_3gpp2_card_t *this) { - this->imsi->destroy(this->imsi); free(this); } @@ -83,14 +149,20 @@ eap_aka_3gpp2_card_t *eap_aka_3gpp2_card_create(eap_aka_3gpp2_functions_t *f) { private_eap_aka_3gpp2_card_t *this = malloc_thing(private_eap_aka_3gpp2_card_t); - this->public.card.get_imsi = (identification_t*(*)(usim_card_t*))get_imsi; - this->public.card.get_quintuplet = (status_t(*)(usim_card_t*, char rand[16], char autn[16], char ck[16], char ik[16], char res[16]))get_quintuplet; - this->public.card.resync = (bool(*)(usim_card_t*, char rand[16], char auts[16]))resync; + this->public.card.get_quintuplet = (status_t(*)(usim_card_t*, identification_t *imsi, char rand[16], char autn[16], char ck[16], char ik[16], char res[16]))get_quintuplet; + this->public.card.resync = (bool(*)(usim_card_t*, identification_t *imsi, char rand[16], char auts[14]))resync; this->public.destroy = (void(*)(eap_aka_3gpp2_card_t*))destroy; - /* this software USIM can act with all identities */ - this->imsi = identification_create_from_encoding(ID_ANY, chunk_empty); this->f = f; + this->seq_check = lib->settings->get_bool(lib->settings, + "charon.plugins.eap_aka_3gpp2.seq_check", +#ifdef SEQ_CHECK /* handle legacy compile time configuration as default */ + TRUE); +#else /* !SEQ_CHECK */ + FALSE); +#endif /* SEQ_CHECK */ + + eap_aka_3gpp2_get_sqn(this->sqn, 0); return &this->public; } diff --git a/src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_functions.c b/src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_functions.c index 17f522abd..10b9c5c83 100644 --- a/src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_functions.c +++ b/src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_functions.c @@ -38,7 +38,7 @@ struct private_eap_aka_3gpp2_functions_t { prf_t *prf; }; -#define PAYLOAD_LENGTH 64 +#define AKA_PAYLOAD_LEN 64 #define F1 0x42 #define F1STAR 0x43 @@ -170,8 +170,8 @@ static void mpz_mod_poly(mpz_t r, mpz_t a, mpz_t b) * Step 3 of the various fx() functions: * XOR the key into the SHA1 IV */ -static void step3(prf_t *prf, u_char k[K_LENGTH], u_char payload[PAYLOAD_LENGTH], - u_int8_t h[HASH_SIZE_SHA1]) +static void step3(prf_t *prf, u_char k[AKA_K_LEN], + u_char payload[AKA_PAYLOAD_LEN], u_int8_t h[HASH_SIZE_SHA1]) { /* use the keyed hasher to build the hash */ prf->set_key(prf, chunk_create(k, sizeof(k))); @@ -211,10 +211,10 @@ static void step4(u_char x[HASH_SIZE_SHA1]) /** * Calculation function for f2(), f3(), f4() */ -static void fx(prf_t *prf, u_char f, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char out[MAC_LENGTH]) +static void fx(prf_t *prf, u_char f, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char out[AKA_MAC_LEN]) { - u_char payload[PAYLOAD_LENGTH]; + u_char payload[AKA_PAYLOAD_LEN]; u_char h[HASH_SIZE_SHA1]; u_char i; @@ -239,15 +239,15 @@ static void fx(prf_t *prf, u_char f, u_char k[K_LENGTH], /** * Calculation function of f1() and f1star() */ -static void f1x(prf_t *prf, u_int8_t f, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char sqn[SQN_LENGTH], - u_char amf[AMF_LENGTH], u_char mac[MAC_LENGTH]) +static void f1x(prf_t *prf, u_int8_t f, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char sqn[AKA_SQN_LEN], + u_char amf[AKA_AMF_LEN], u_char mac[AKA_MAC_LEN]) { /* generate MAC = f1(FMK, SQN, RAND, AMF) * K is loaded into hashers IV; FMK, RAND, SQN, AMF are XORed in a 512-bit * payload which gets hashed */ - u_char payload[PAYLOAD_LENGTH]; + u_char payload[AKA_PAYLOAD_LEN]; u_char h[HASH_SIZE_SHA1]; memset(payload, 0x5c, sizeof(payload)); @@ -265,10 +265,10 @@ static void f1x(prf_t *prf, u_int8_t f, u_char k[K_LENGTH], /** * Calculation function of f5() and f5star() */ -static void f5x(prf_t *prf, u_char f, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char ak[AK_LENGTH]) +static void f5x(prf_t *prf, u_char f, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char ak[AKA_AK_LEN]) { - u_char payload[PAYLOAD_LENGTH]; + u_char payload[AKA_PAYLOAD_LEN]; u_char h[HASH_SIZE_SHA1]; memset(payload, 0x5c, sizeof(payload)); @@ -284,9 +284,9 @@ static void f5x(prf_t *prf, u_char f, u_char k[K_LENGTH], /** * Calculate MAC from RAND, SQN, AMF using K */ -static void f1(private_eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char sqn[SQN_LENGTH], - u_char amf[AMF_LENGTH], u_char mac[MAC_LENGTH]) +static void f1(private_eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char sqn[AKA_SQN_LEN], + u_char amf[AKA_AMF_LEN], u_char mac[AKA_MAC_LEN]) { f1x(this->prf, F1, k, rand, sqn, amf, mac); DBG3(DBG_IKE, "MAC %b", mac, sizeof(mac)); @@ -295,9 +295,9 @@ static void f1(private_eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], /** * Calculate MACS from RAND, SQN, AMF using K */ -static void f1star(private_eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char sqn[SQN_LENGTH], - u_char amf[AMF_LENGTH], u_char macs[MAC_LENGTH]) +static void f1star(private_eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char sqn[AKA_SQN_LEN], + u_char amf[AKA_AMF_LEN], u_char macs[AKA_MAC_LEN]) { f1x(this->prf, F1STAR, k, rand, sqn, amf, macs); DBG3(DBG_IKE, "MACS %b", macs, sizeof(macs)); @@ -306,8 +306,8 @@ static void f1star(private_eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], /** * Calculate RES from RAND using K */ -static void f2(private_eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char res[RES_LENGTH]) +static void f2(private_eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char res[AKA_RES_LEN]) { fx(this->prf, F2, k, rand, res); DBG3(DBG_IKE, "RES %b", res, sizeof(res)); @@ -316,8 +316,8 @@ static void f2(private_eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], /** * Calculate CK from RAND using K */ -static void f3(private_eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char ck[CK_LENGTH]) +static void f3(private_eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char ck[AKA_CK_LEN]) { fx(this->prf, F3, k, rand, ck); DBG3(DBG_IKE, "CK %b", ck, sizeof(ck)); @@ -326,8 +326,8 @@ static void f3(private_eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], /** * Calculate IK from RAND using K */ -static void f4(private_eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char ik[IK_LENGTH]) +static void f4(private_eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char ik[AKA_IK_LEN]) { fx(this->prf, F4, k, rand, ik); DBG3(DBG_IKE, "IK %b", ik, sizeof(ik)); @@ -336,8 +336,8 @@ static void f4(private_eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], /** * Calculate AK from a RAND using K */ -static void f5(private_eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char ak[AK_LENGTH]) +static void f5(private_eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char ak[AKA_AK_LEN]) { f5x(this->prf, F5, k, rand, ak); DBG3(DBG_IKE, "AK %b", ak, sizeof(ak)); @@ -346,8 +346,8 @@ static void f5(private_eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], /** * Calculate AKS from a RAND using K */ -static void f5star(private_eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char aks[AK_LENGTH]) +static void f5star(private_eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char aks[AKA_AK_LEN]) { f5x(this->prf, F5STAR, k, rand, aks); DBG3(DBG_IKE, "AKS %b", aks, sizeof(aks)); @@ -372,13 +372,13 @@ eap_aka_3gpp2_functions_t *eap_aka_3gpp2_functions_create() this = malloc_thing(private_eap_aka_3gpp2_functions_t); - this->public.f1 = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], u_char rand[RAND_LENGTH], u_char sqn[SQN_LENGTH], u_char amf[AMF_LENGTH], u_char mac[MAC_LENGTH]))f1; - this->public.f1star = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], u_char rand[RAND_LENGTH], u_char sqn[SQN_LENGTH], u_char amf[AMF_LENGTH], u_char macs[MAC_LENGTH]))f1star; - this->public.f2 = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], u_char rand[RAND_LENGTH], u_char res[RES_LENGTH]))f2; - this->public.f3 = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], u_char rand[RAND_LENGTH], u_char ck[CK_LENGTH]))f3; - this->public.f4 = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], u_char rand[RAND_LENGTH], u_char ik[IK_LENGTH]))f4; - this->public.f5 = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], u_char rand[RAND_LENGTH], u_char ak[AK_LENGTH]))f5; - this->public.f5star = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], u_char rand[RAND_LENGTH], u_char aks[AK_LENGTH]))f5star; + this->public.f1 = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], u_char rand[AKA_RAND_LEN], u_char sqn[AKA_SQN_LEN], u_char amf[AKA_AMF_LEN], u_char mac[AKA_MAC_LEN]))f1; + this->public.f1star = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], u_char rand[AKA_RAND_LEN], u_char sqn[AKA_SQN_LEN], u_char amf[AKA_AMF_LEN], u_char macs[AKA_MAC_LEN]))f1star; + this->public.f2 = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], u_char rand[AKA_RAND_LEN], u_char res[AKA_RES_LEN]))f2; + this->public.f3 = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], u_char rand[AKA_RAND_LEN], u_char ck[AKA_CK_LEN]))f3; + this->public.f4 = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], u_char rand[AKA_RAND_LEN], u_char ik[AKA_IK_LEN]))f4; + this->public.f5 = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], u_char rand[AKA_RAND_LEN], u_char ak[AKA_AK_LEN]))f5; + this->public.f5star = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], u_char rand[AKA_RAND_LEN], u_char aks[AKA_AK_LEN]))f5star; this->public.destroy = (void(*)(eap_aka_3gpp2_functions_t*))destroy; this->prf = lib->crypto->create_prf(lib->crypto, PRF_KEYED_SHA1); diff --git a/src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_functions.h b/src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_functions.h index e870e1e35..4b3a08087 100644 --- a/src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_functions.h +++ b/src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_functions.h @@ -21,21 +21,14 @@ #ifndef EAP_AKA_3GPP2_FUNCTIONS_H_ #define EAP_AKA_3GPP2_FUNCTIONS_H_ -#include <utils/enumerator.h> -#include <utils/identification.h> +#include <sa/authenticators/eap/usim_manager.h> -#define RAND_LENGTH 16 -#define RES_LENGTH 16 -#define SQN_LENGTH 6 -#define K_LENGTH 16 -#define MAC_LENGTH 8 -#define CK_LENGTH 16 -#define IK_LENGTH 16 -#define AK_LENGTH 6 -#define AMF_LENGTH 2 -#define FMK_LENGTH 4 -#define AUTN_LENGTH (SQN_LENGTH + AMF_LENGTH + MAC_LENGTH) -#define AUTS_LENGTH (SQN_LENGTH + MAC_LENGTH) +#define AKA_SQN_LEN 6 +#define AKA_K_LEN 16 +#define AKA_MAC_LEN 8 +#define AKA_AK_LEN 6 +#define AKA_AMF_LEN 2 +#define AKA_FMK_LEN 4 typedef struct eap_aka_3gpp2_functions_t eap_aka_3gpp2_functions_t; @@ -53,9 +46,9 @@ struct eap_aka_3gpp2_functions_t { * @param amf authentication management field * @param mac buffer receiving mac MAC */ - void (*f1)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char sqn[SQN_LENGTH], - u_char amf[AMF_LENGTH], u_char mac[MAC_LENGTH]); + void (*f1)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char sqn[AKA_SQN_LEN], + u_char amf[AKA_AMF_LEN], u_char mac[AKA_MAC_LEN]); /** * Calculate MACS from RAND, SQN, AMF using K @@ -66,9 +59,9 @@ struct eap_aka_3gpp2_functions_t { * @param amf authentication management field * @param macs buffer receiving resynchronization mac MACS */ - void (*f1star)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char sqn[SQN_LENGTH], - u_char amf[AMF_LENGTH], u_char macs[MAC_LENGTH]); + void (*f1star)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char sqn[AKA_SQN_LEN], + u_char amf[AKA_AMF_LEN], u_char macs[AKA_MAC_LEN]); /** * Calculate RES from RAND using K @@ -77,8 +70,8 @@ struct eap_aka_3gpp2_functions_t { * @param rand random value RAND * @param macs buffer receiving result RES */ - void (*f2)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char res[RES_LENGTH]); + void (*f2)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char res[AKA_RES_LEN]); /** * Calculate CK from RAND using K * @@ -86,8 +79,8 @@ struct eap_aka_3gpp2_functions_t { * @param rand random value RAND * @param macs buffer receiving encryption key CK */ - void (*f3)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char ck[CK_LENGTH]); + void (*f3)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char ck[AKA_CK_LEN]); /** * Calculate IK from RAND using K * @@ -95,8 +88,8 @@ struct eap_aka_3gpp2_functions_t { * @param rand random value RAND * @param macs buffer receiving integrity key IK */ - void (*f4)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char ik[IK_LENGTH]); + void (*f4)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char ik[AKA_IK_LEN]); /** * Calculate AK from a RAND using K * @@ -104,8 +97,8 @@ struct eap_aka_3gpp2_functions_t { * @param rand random value RAND * @param macs buffer receiving anonymity key AK */ - void (*f5)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char ak[AK_LENGTH]); + void (*f5)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char ak[AKA_AK_LEN]); /** * Calculate AKS from a RAND using K * @@ -113,8 +106,8 @@ struct eap_aka_3gpp2_functions_t { * @param rand random value RAND * @param macs buffer receiving resynchronization anonymity key AKS */ - void (*f5star)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH], - u_char rand[RAND_LENGTH], u_char aks[AK_LENGTH]); + void (*f5star)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], + u_char rand[AKA_RAND_LEN], u_char aks[AKA_AK_LEN]); /** * Destroy a eap_aka_3gpp2_functions_t. diff --git a/src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_provider.c b/src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_provider.c index cf7261a37..de87f0538 100644 --- a/src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_provider.c +++ b/src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_provider.c @@ -15,6 +15,9 @@ #include "eap_aka_3gpp2_provider.h" +#include <daemon.h> +#include <credentials/keys/shared_key.h> + typedef struct private_eap_aka_3gpp2_provider_t private_eap_aka_3gpp2_provider_t; /** @@ -31,25 +34,139 @@ struct private_eap_aka_3gpp2_provider_t { * AKA functions */ eap_aka_3gpp2_functions_t *f; + + /** + * time based SQN, we use the same for all peers + */ + char sqn[AKA_SQN_LEN]; }; +/** Authentication management field */ +static char amf[AKA_AMF_LEN] = {0x00, 0x01}; + +/** + * Get a shared key K from the credential database + */ +bool eap_aka_3gpp2_get_k(identification_t *id, char k[AKA_K_LEN]) +{ + shared_key_t *shared; + chunk_t key; + + shared = charon->credentials->get_shared(charon->credentials, + SHARED_EAP, id, NULL); + if (shared == NULL) + { + return FALSE; + } + key = shared->get_key(shared); + memset(k, '\0', sizeof(k)); + memcpy(k, key.ptr, min(key.len, sizeof(k))); + shared->destroy(shared); + return TRUE; +} + +/** + * get SQN using current time + */ +void eap_aka_3gpp2_get_sqn(char sqn[AKA_SQN_LEN], int offset) +{ + timeval_t time; + + time_monotonic(&time); + /* set sqn to an integer containing seconds followed by most + * significant useconds */ + time.tv_sec = htonl(time.tv_sec + offset); + /* usec's are never larger than 0x000f423f, so we shift the 12 first bits */ + time.tv_usec <<= 12; + time.tv_usec = htonl(time.tv_usec); + memcpy(sqn, &time.tv_sec, 4); + memcpy(sqn + 4, &time.tv_usec, 2); +} + /** * Implementation of usim_provider_t.get_quintuplet */ static bool get_quintuplet(private_eap_aka_3gpp2_provider_t *this, - identification_t *imsi, char rand[16], char xres[16], - char ck[16], char ik[16], char autn[16]) + identification_t *imsi, char rand[AKA_RAND_LEN], + char xres[AKA_RES_LEN], char ck[AKA_CK_LEN], + char ik[AKA_IK_LEN], char autn[AKA_AUTN_LEN]) { - return FALSE; + rng_t *rng; + char mac[AKA_MAC_LEN], ak[AKA_AK_LEN], k[AKA_K_LEN]; + + /* generate RAND: we use a registered RNG, not f0() proposed in S.S0055 */ + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) + { + DBG1(DBG_IKE, "generating RAND for AKA failed"); + return FALSE; + } + rng->get_bytes(rng, AKA_RAND_LEN, rand); + rng->destroy(rng); + + if (!eap_aka_3gpp2_get_k(imsi, k)) + { + DBG1(DBG_IKE, "no EAP key found for %Y to authenticate with AKA", imsi); + return FALSE; + } + + /* MAC */ + this->f->f1(this->f, k, rand, this->sqn, amf, mac); + /* AK */ + this->f->f5(this->f, k, rand, ak); + /* XRES as expected from client */ + this->f->f2(this->f, k, rand, xres); + /* AUTN = (SQN xor AK) || AMF || MAC */ + memcpy(autn, this->sqn, sizeof(this->sqn)); + memxor(autn, ak, sizeof(ak)); + memcpy(autn + sizeof(this->sqn), amf, sizeof(amf)); + memcpy(autn + sizeof(this->sqn) + sizeof(amf), mac, sizeof(mac)); + DBG3(DBG_IKE, "AUTN %b", autn, sizeof(autn)); + /* CK/IK */ + this->f->f3(this->f, k, rand, ck); + DBG3(DBG_IKE, "CK %b", ck, sizeof(ck)); + this->f->f4(this->f, k, rand, ik); + DBG3(DBG_IKE, "IK %b", ik, sizeof(ik)); + + return TRUE; } /** * Implementation of usim_provider_t.resync */ static bool resync(private_eap_aka_3gpp2_provider_t *this, - identification_t *imsi, char rand[16], char auts[16]) + identification_t *imsi, char rand[AKA_RAND_LEN], + char auts[AKA_AUTS_LEN]) { - return FALSE; + char *sqn, *macs; + char aks[AKA_AK_LEN], k[AKA_K_LEN], amf[AKA_AMF_LEN], xmacs[AKA_MAC_LEN]; + + if (!eap_aka_3gpp2_get_k(imsi, k)) + { + DBG1(DBG_IKE, "no EAP key found for %Y to authenticate with AKA", imsi); + return FALSE; + } + + /* AUTHS = (AK xor SQN) | MAC */ + sqn = auts; + macs = auts + AKA_SQN_LEN; + this->f->f5star(this->f, k, rand, aks); + memxor(sqn, aks, sizeof(aks)); + + /* verify XMACS, AMF of zero is used in resynchronization */ + memset(amf, 0, sizeof(amf)); + this->f->f1star(this->f, k, rand, sqn, amf, xmacs); + if (!memeq(macs, xmacs, sizeof(xmacs))) + { + DBG1(DBG_IKE, "received MACS does not match XMACS"); + DBG3(DBG_IKE, "MACS %b XMACS %b", + macs, AKA_MAC_LEN, xmacs, sizeof(xmacs)); + return FALSE; + } + /* update stored SQN to received SQN + 1 */ + memcpy(this->sqn, sqn, AKA_SQN_LEN); + chunk_increment(chunk_create(this->sqn, AKA_SQN_LEN)); + return TRUE; } /** @@ -69,10 +186,12 @@ eap_aka_3gpp2_provider_t *eap_aka_3gpp2_provider_create( private_eap_aka_3gpp2_provider_t *this = malloc_thing(private_eap_aka_3gpp2_provider_t); this->public.provider.get_quintuplet = (bool(*)(usim_provider_t*, identification_t *imsi, char rand[16], char xres[16], char ck[16], char ik[16], char autn[16]))get_quintuplet; - this->public.provider.resync = (bool(*)(usim_provider_t*, identification_t *imsi, char rand[16], char auts[16]))resync; + this->public.provider.resync = (bool(*)(usim_provider_t*, identification_t *imsi, char rand[16], char auts[14]))resync; this->public.destroy = (void(*)(eap_aka_3gpp2_provider_t*))destroy; this->f = f; + /* use an offset to accept clock skew between client/server without resync */ + eap_aka_3gpp2_get_sqn(this->sqn, 180); return &this->public; } diff --git a/src/charon/sa/authenticators/eap/usim_manager.h b/src/charon/sa/authenticators/eap/usim_manager.h index 6a2e0e573..6d1261935 100644 --- a/src/charon/sa/authenticators/eap/usim_manager.h +++ b/src/charon/sa/authenticators/eap/usim_manager.h @@ -28,6 +28,13 @@ typedef struct usim_manager_t usim_manager_t; typedef struct usim_card_t usim_card_t; typedef struct usim_provider_t usim_provider_t; +#define AKA_RAND_LEN 16 +#define AKA_RES_LEN 16 +#define AKA_CK_LEN 16 +#define AKA_IK_LEN 16 +#define AKA_AUTN_LEN 16 +#define AKA_AUTS_LEN 14 + /** * Interface for a USIM card (used by EAP-AKA client). */ @@ -45,8 +52,9 @@ struct usim_provider_t { * @return TRUE if quintuplet generated successfully */ bool (*get_quintuplet)(usim_provider_t *this, identification_t *imsi, - char rand[16], char xres[16], - char ck[16], char ik[16], char autn[16]); + char rand[AKA_RAND_LEN], char xres[AKA_RES_LEN], + char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], + char autn[AKA_AUTN_LEN]); /** * Process resynchroniusation request of a peer. @@ -57,7 +65,7 @@ struct usim_provider_t { * @return TRUE if resynchronized successfully */ bool (*resync)(usim_provider_t *this, identification_t *imsi, - char rand[16], char auts[16]); + char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]); }; /** @@ -66,18 +74,12 @@ struct usim_provider_t { struct usim_card_t { /** - * Get the IMSI of this USIM. - * - * @return IMSI this USIM belongs to - */ - identification_t *(*get_imsi)(usim_card_t *this); - - /** * Process authentication data and complete the quintuplet. * * If the received sequence number (in autn) is out of synf, INVALID_STATE * is returned. * + * @param imsi peer identity requesting quintuplet for * @param rand random value rand * @param autn authentication token autn * @param ck buffer receiving encryption key ck @@ -85,17 +87,21 @@ struct usim_card_t { * @param res buffer receiving authentication result res * @return SUCCESS, FAILED, or INVALID_STATE if out of sync */ - status_t (*get_quintuplet)(usim_card_t *this, char rand[16], char autn[16], - char ck[16], char ik[16], char res[16]); + status_t (*get_quintuplet)(usim_card_t *this, identification_t *imsi, + char rand[AKA_RAND_LEN], char autn[AKA_AUTN_LEN], + char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], + char res[AKA_RES_LEN]); /** * Request parameter to start resynchronization. * + * @param imsi peer identity requesting quintuplet for * @param in random value rand * @param auts resynchronization parameter auts * @return TRUE if parameter generated successfully */ - bool (*resync)(usim_card_t *this, char rand[16], char auts[16]); + bool (*resync)(usim_card_t *this, identification_t *imsi, + char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]); }; /** |