Separated 3gpp2 USIM card and provider functionality
authorMartin Willi <martin@strongswan.org>
Thu, 8 Oct 2009 14:49:29 +0000 (16:49 +0200)
committerMartin Willi <martin@strongswan.org>
Fri, 9 Oct 2009 11:02:20 +0000 (13:02 +0200)
src/charon/plugins/eap_aka/eap_aka.c
src/charon/plugins/eap_aka/eap_aka.h
src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_card.c
src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_functions.c
src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_functions.h
src/charon/plugins/eap_aka_3gpp2/eap_aka_3gpp2_provider.c
src/charon/sa/authenticators/eap/usim_manager.h

index 1cbe472..f7a1e2d 100644 (file)
@@ -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
 #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)
        {
index 7686802..e12270c 100644 (file)
@@ -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 {
 
index 4d584f2..aeaa683 100644 (file)
@@ -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;
 }
index 17f522a..10b9c5c 100644 (file)
@@ -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);
index e870e1e..4b3a080 100644 (file)
 #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.
index cf7261a..de87f05 100644 (file)
@@ -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;
 }
index 6a2e0e5..6d12619 100644 (file)
@@ -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]);
 };
 
 /**