Moving charon to libcharon.

This commit is contained in:
Tobias Brunner
2010-03-19 13:34:52 +01:00
parent 7c11d10eb8
commit 08c5572602
480 changed files with 0 additions and 0 deletions
@@ -0,0 +1,19 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-eap-aka-3gpp2.la
else
plugin_LTLIBRARIES = libstrongswan-eap-aka-3gpp2.la
endif
libstrongswan_eap_aka_3gpp2_la_SOURCES = \
eap_aka_3gpp2_plugin.h eap_aka_3gpp2_plugin.c \
eap_aka_3gpp2_card.h eap_aka_3gpp2_card.c \
eap_aka_3gpp2_provider.h eap_aka_3gpp2_provider.c \
eap_aka_3gpp2_functions.h eap_aka_3gpp2_functions.c
libstrongswan_eap_aka_3gpp2_la_LDFLAGS = -module -avoid-version
libstrongswan_eap_aka_3gpp2_la_LIBADD = -lgmp
@@ -0,0 +1,178 @@
/*
* Copyright (C) 2008-2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "eap_aka_3gpp2_card.h"
#include <daemon.h>
typedef struct private_eap_aka_3gpp2_card_t private_eap_aka_3gpp2_card_t;
/**
* Private data of an eap_aka_3gpp2_card_t object.
*/
struct private_eap_aka_3gpp2_card_t {
/**
* Public eap_aka_3gpp2_card_t interface.
*/
eap_aka_3gpp2_card_t public;
/**
* AKA functions
*/
eap_aka_3gpp2_functions_t *f;
/**
* do sequence number checking?
*/
bool seq_check;
/**
* SQN stored in this pseudo-USIM
*/
char sqn[AKA_SQN_LEN];
};
/**
* Functions from eap_aka_3gpp2_provider.c
*/
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 sim_card_t.get_quintuplet
*/
static status_t get_quintuplet(private_eap_aka_3gpp2_card_t *this,
identification_t *id, char rand[AKA_RAND_LEN],
char autn[AKA_AUTN_LEN], char ck[AKA_CK_LEN],
char ik[AKA_IK_LEN], char res[AKA_RES_MAX],
int *res_len)
{
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(id, k))
{
DBG1(DBG_IKE, "no EAP key found for %Y to authenticate with AKA", id);
return FAILED;
}
/* AUTN = SQN xor AK | AMF | MAC */
DBG3(DBG_IKE, "received autn %b", autn, AKA_AUTN_LEN);
DBG3(DBG_IKE, "using K %b", k, AKA_K_LEN);
DBG3(DBG_IKE, "using rand %b", rand, AKA_RAND_LEN);
memcpy(sqn, autn, AKA_SQN_LEN);
amf = autn + AKA_SQN_LEN;
mac = autn + AKA_SQN_LEN + 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, AKA_AK_LEN);
memxor(sqn, ak, AKA_SQN_LEN);
DBG3(DBG_IKE, "using sqn %b", sqn, AKA_SQN_LEN);
/* calculate expected MAC and compare against received one */
this->f->f1(this->f, k, rand, sqn, amf, xmac);
if (!memeq(mac, xmac, AKA_MAC_LEN))
{
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, AKA_SQN_LEN) >= 0)
{
DBG3(DBG_IKE, "received SQN %b\ncurrent SQN %b",
sqn, AKA_SQN_LEN, this->sqn, AKA_SQN_LEN);
return INVALID_STATE;
}
/* update stored SQN to the received one */
memcpy(this->sqn, sqn, AKA_SQN_LEN);
/* CK/IK */
this->f->f3(this->f, k, rand, ck);
this->f->f4(this->f, k, rand, ik);
/* calculate RES */
this->f->f2(this->f, k, rand, res);
*res_len = AKA_RES_MAX;
return SUCCESS;
}
/**
* Implementation of sim_card_t.resync
*/
static bool resync(private_eap_aka_3gpp2_card_t *this, identification_t *id,
char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN])
{
char amf[AKA_AMF_LEN], k[AKA_K_LEN], aks[AKA_AK_LEN], macs[AKA_MAC_LEN];
if (!eap_aka_3gpp2_get_k(id, k))
{
DBG1(DBG_IKE, "no EAP key found for %Y to resync AKA", id);
return FALSE;
}
/* AMF is set to zero in resync */
memset(amf, 0, AKA_AMF_LEN);
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, AKA_SQN_LEN);
memxor(auts, aks, AKA_AK_LEN);
memcpy(auts + AKA_AK_LEN, macs, AKA_MAC_LEN);
return TRUE;
}
/**
* Implementation of eap_aka_3gpp2_card_t.destroy.
*/
static void destroy(private_eap_aka_3gpp2_card_t *this)
{
free(this);
}
/**
* See header
*/
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_triplet = (bool(*)(sim_card_t*, identification_t *id, char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN]))return_false;
this->public.card.get_quintuplet = (status_t(*)(sim_card_t*, identification_t *id, char rand[AKA_RAND_LEN], char autn[AKA_AUTN_LEN], char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], char res[AKA_RES_MAX], int *res_len))get_quintuplet;
this->public.card.resync = (bool(*)(sim_card_t*, identification_t *id, char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]))resync;
this->public.card.get_pseudonym = (identification_t*(*)(sim_card_t*, identification_t *id))return_null;
this->public.card.set_pseudonym = (void(*)(sim_card_t*, identification_t *id, identification_t *pseudonym))nop;
this->public.card.get_reauth = (identification_t*(*)(sim_card_t*, identification_t *id, char mk[HASH_SIZE_SHA1], u_int16_t *counter))return_null;
this->public.card.set_reauth = (void(*)(sim_card_t*, identification_t *id, identification_t* next, char mk[HASH_SIZE_SHA1], u_int16_t counter))nop;
this->public.destroy = (void(*)(eap_aka_3gpp2_card_t*))destroy;
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;
}
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2008-2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/**
* @defgroup eap_aka_3gpp2_card eap_aka_3gpp2_card
* @{ @ingroup eap_aka_3gpp2
*/
#ifndef EAP_AKA_3GPP2_CARD_H_
#define EAP_AKA_3GPP2_CARD_H_
#include "eap_aka_3gpp2_functions.h"
#include <sa/authenticators/eap/sim_manager.h>
typedef struct eap_aka_3gpp2_card_t eap_aka_3gpp2_card_t;
/**
* SIM card implementation using a set of AKA functions.
*/
struct eap_aka_3gpp2_card_t {
/**
* Implements sim_card_t interface
*/
sim_card_t card;
/**
* Destroy a eap_aka_3gpp2_card_t.
*/
void (*destroy)(eap_aka_3gpp2_card_t *this);
};
/**
* Create a eap_aka_3gpp2_card instance.
*
* @param f AKA functions
*/
eap_aka_3gpp2_card_t *eap_aka_3gpp2_card_create(eap_aka_3gpp2_functions_t *f);
#endif /** EAP_AKA_3GPP2_CARD_H_ @}*/
@@ -0,0 +1,394 @@
/*
* Copyright (C) 2008-2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "eap_aka_3gpp2_functions.h"
#include <gmp.h>
#include <limits.h>
#include <daemon.h>
typedef struct private_eap_aka_3gpp2_functions_t private_eap_aka_3gpp2_functions_t;
/**
* Private data of an eap_aka_3gpp2_functions_t object.
*/
struct private_eap_aka_3gpp2_functions_t {
/**
* Public eap_aka_3gpp2_functions_t interface.
*/
eap_aka_3gpp2_functions_t public;
/**
* Used keyed SHA1 function, as PRF
*/
prf_t *prf;
};
#define AKA_PAYLOAD_LEN 64
#define F1 0x42
#define F1STAR 0x43
#define F2 0x44
#define F3 0x45
#define F4 0x46
#define F5 0x47
#define F5STAR 0x48
/** Family key, as proposed in S.S0055 */
static chunk_t fmk = chunk_from_chars(0x41, 0x48, 0x41, 0x47);
/**
* 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 3 of the various fx() functions:
* XOR the key into the SHA1 IV
*/
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, AKA_K_LEN));
prf->get_bytes(prf, chunk_create(payload, AKA_PAYLOAD_LEN), h);
}
/**
* Step 4 of the various fx() functions:
* Polynomial whiten calculations
*/
static void step4(u_char x[HASH_SIZE_SHA1])
{
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);
}
/**
* Calculation function for f2(), f3(), f4()
*/
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[AKA_PAYLOAD_LEN];
u_char h[HASH_SIZE_SHA1];
u_char i;
for (i = 0; i < 2; i++)
{
memset(payload, 0x5c, AKA_PAYLOAD_LEN);
payload[11] ^= f;
memxor(payload + 12, fmk.ptr, fmk.len);
memxor(payload + 24, rand, AKA_RAND_LEN);
payload[3] ^= i;
payload[19] ^= i;
payload[35] ^= i;
payload[51] ^= i;
step3(prf, k, payload, h);
step4(h);
memcpy(out + i * 8, h, 8);
}
}
/**
* Calculation function of f1() and f1star()
*/
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[AKA_PAYLOAD_LEN];
u_char h[HASH_SIZE_SHA1];
memset(payload, 0x5c, AKA_PAYLOAD_LEN);
payload[11] ^= f;
memxor(payload + 12, fmk.ptr, fmk.len);
memxor(payload + 16, rand, AKA_RAND_LEN);
memxor(payload + 34, sqn, AKA_SQN_LEN);
memxor(payload + 42, amf, AKA_AMF_LEN);
step3(prf, k, payload, h);
step4(h);
memcpy(mac, h, AKA_MAC_LEN);
}
/**
* Calculation function of f5() and f5star()
*/
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[AKA_PAYLOAD_LEN];
u_char h[HASH_SIZE_SHA1];
memset(payload, 0x5c, AKA_PAYLOAD_LEN);
payload[11] ^= f;
memxor(payload + 12, fmk.ptr, fmk.len);
memxor(payload + 16, rand, AKA_RAND_LEN);
step3(prf, k, payload, h);
step4(h);
memcpy(ak, h, AKA_AK_LEN);
}
/**
* Calculate MAC from RAND, SQN, AMF using K
*/
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, AKA_MAC_LEN);
}
/**
* Calculate MACS from RAND, SQN, AMF using K
*/
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, AKA_MAC_LEN);
}
/**
* Calculate RES from RAND using K
*/
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_MAX])
{
fx(this->prf, F2, k, rand, res);
DBG3(DBG_IKE, "RES %b", res, AKA_RES_MAX);
}
/**
* Calculate CK from RAND using K
*/
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, AKA_CK_LEN);
}
/**
* Calculate IK from RAND using K
*/
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, AKA_IK_LEN);
}
/**
* Calculate AK from a RAND using K
*/
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, AKA_AK_LEN);
}
/**
* Calculate AKS from a RAND using K
*/
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, AKA_AK_LEN);
}
/**
* Implementation of eap_aka_3gpp2_functions_t.destroy.
*/
static void destroy(private_eap_aka_3gpp2_functions_t *this)
{
this->prf->destroy(this->prf);
free(this);
}
/**
* See header
*/
eap_aka_3gpp2_functions_t *eap_aka_3gpp2_functions_create()
{
private_eap_aka_3gpp2_functions_t *this;
this = malloc_thing(private_eap_aka_3gpp2_functions_t);
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_MAX]))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);
if (!this->prf)
{
DBG1(DBG_CFG, "%N not supported, unable to use 3GPP2 algorithm",
pseudo_random_function_names, PRF_KEYED_SHA1);
free(this);
return NULL;
}
return &this->public;
}
@@ -0,0 +1,125 @@
/*
* Copyright (C) 2008-2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/**
* @defgroup eap_aka_3gpp2_functions eap_aka_3gpp2_functions
* @{ @ingroup eap_aka_3gpp2
*/
#ifndef EAP_AKA_3GPP2_FUNCTIONS_H_
#define EAP_AKA_3GPP2_FUNCTIONS_H_
#include <sa/authenticators/eap/sim_manager.h>
#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;
/**
* f1-f5(), f1*() and f5*() functions from the 3GPP2 (S.S0055) standard.
*/
struct eap_aka_3gpp2_functions_t {
/**
* Calculate MAC from RAND, SQN, AMF using K.
*
* @param k secret key K
* @param rand random value rand
* @param sqn sequence number
* @param amf authentication management field
* @param mac buffer receiving mac MAC
*/
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
*
* @param k secret key K
* @param rand random value RAND
* @param sqn sequence number
* @param amf authentication management field
* @param macs buffer receiving resynchronization mac MACS
*/
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
*
* @param k secret key K
* @param rand random value RAND
* @param res buffer receiving result RES, uses full 128 bit
*/
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_MAX]);
/**
* Calculate CK from RAND using K
*
* @param k secret key K
* @param rand random value RAND
* @param macs buffer receiving encryption key CK
*/
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
*
* @param k secret key K
* @param rand random value RAND
* @param macs buffer receiving integrity key IK
*/
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
*
* @param k secret key K
* @param rand random value RAND
* @param macs buffer receiving anonymity key AK
*/
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
*
* @param k secret key K
* @param rand random value RAND
* @param macs buffer receiving resynchronization anonymity key AKS
*/
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.
*/
void (*destroy)(eap_aka_3gpp2_functions_t *this);
};
/**
* Create a eap_aka_3gpp2_functions instance.
*
* @return function set, NULL on error
*/
eap_aka_3gpp2_functions_t *eap_aka_3gpp2_functions_create();
#endif /** EAP_AKA_3GPP2_FUNCTIONS_H_ @}*/
@@ -0,0 +1,87 @@
/*
* Copyright (C) 2008-2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "eap_aka_3gpp2_plugin.h"
#include "eap_aka_3gpp2_card.h"
#include "eap_aka_3gpp2_provider.h"
#include "eap_aka_3gpp2_functions.h"
#include <daemon.h>
typedef struct private_eap_aka_3gpp2_t private_eap_aka_3gpp2_t;
/**
* Private data of an eap_aka_3gpp2_t object.
*/
struct private_eap_aka_3gpp2_t {
/**
* Public eap_aka_3gpp2_plugin_t interface.
*/
eap_aka_3gpp2_plugin_t public;
/**
* SIM card
*/
eap_aka_3gpp2_card_t *card;
/**
* SIM provider
*/
eap_aka_3gpp2_provider_t *provider;
/**
* AKA functions
*/
eap_aka_3gpp2_functions_t *functions;
};
/**
* Implementation of eap_aka_3gpp2_t.destroy.
*/
static void destroy(private_eap_aka_3gpp2_t *this)
{
charon->sim->remove_card(charon->sim, &this->card->card);
charon->sim->remove_provider(charon->sim, &this->provider->provider);
this->card->destroy(this->card);
this->provider->destroy(this->provider);
this->functions->destroy(this->functions);
free(this);
}
/**
* See header
*/
plugin_t *eap_aka_3gpp2_plugin_create()
{
private_eap_aka_3gpp2_t *this = malloc_thing(private_eap_aka_3gpp2_t);
this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
this->functions = eap_aka_3gpp2_functions_create();
if (!this->functions)
{
free(this);
return NULL;
}
this->card = eap_aka_3gpp2_card_create(this->functions);
this->provider = eap_aka_3gpp2_provider_create(this->functions);
charon->sim->add_card(charon->sim, &this->card->card);
charon->sim->add_provider(charon->sim, &this->provider->provider);
return &this->public.plugin;
}
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2008-2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/**
* @defgroup eap_aka_3gpp2 eap_aka_3gpp2
* @ingroup cplugins
*
* @defgroup eap_aka_3gpp2_plugin eap_aka_3gpp2_plugin
* @{ @ingroup eap_aka_3gpp2
*/
#ifndef EAP_AKA_3GPP2_PLUGIN_H_
#define EAP_AKA_3GPP2_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct eap_aka_3gpp2_plugin_t eap_aka_3gpp2_plugin_t;
/**
* Plugin to provide a SIM card/provider using the 3GPP2 (S.S0055) standard.
*
* This plugin implements the standard of the 3GPP2 (S.S0055) and not the one
* of 3GGP, completely in software using the libgmp library..
* 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.
* 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).
*/
struct eap_aka_3gpp2_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** EAP_AKA_3GPP2_PLUGIN_H_ @}*/
@@ -0,0 +1,204 @@
/*
* Copyright (C) 2008-2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#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;
/**
* Private data of an eap_aka_3gpp2_provider_t object.
*/
struct private_eap_aka_3gpp2_provider_t {
/**
* Public eap_aka_3gpp2_provider_t interface.
*/
eap_aka_3gpp2_provider_t public;
/**
* 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', AKA_K_LEN);
memcpy(k, key.ptr, min(key.len, AKA_K_LEN));
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;
gettimeofday(&time, NULL);
/* set sqn to an integer containing 4 bytes seconds + 2 bytes usecs */
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 = htonl(time.tv_usec << 12);
memcpy(sqn, (char*)&time.tv_sec + sizeof(time_t) - 4, 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 *id, char rand[AKA_RAND_LEN],
char xres[AKA_RES_MAX], int *xres_len,
char ck[AKA_CK_LEN], char ik[AKA_IK_LEN],
char autn[AKA_AUTN_LEN])
{
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(id, k))
{
DBG1(DBG_IKE, "no EAP key found for %Y to authenticate with AKA", id);
return FALSE;
}
DBG3(DBG_IKE, "generated rand %b", rand, AKA_RAND_LEN);
DBG3(DBG_IKE, "using K %b", k, AKA_K_LEN);
/* 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);
*xres_len = AKA_RES_MAX;
/* AUTN = (SQN xor AK) || AMF || MAC */
memcpy(autn, this->sqn, AKA_SQN_LEN);
memxor(autn, ak, AKA_AK_LEN);
memcpy(autn + AKA_SQN_LEN, amf, AKA_AMF_LEN);
memcpy(autn + AKA_SQN_LEN + AKA_AMF_LEN, mac, AKA_MAC_LEN);
DBG3(DBG_IKE, "AUTN %b", autn, AKA_AUTN_LEN);
/* CK/IK */
this->f->f3(this->f, k, rand, ck);
this->f->f4(this->f, k, rand, ik);
return TRUE;
}
/**
* Implementation of usim_provider_t.resync
*/
static bool resync(private_eap_aka_3gpp2_provider_t *this,
identification_t *id, char rand[AKA_RAND_LEN],
char auts[AKA_AUTS_LEN])
{
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(id, k))
{
DBG1(DBG_IKE, "no EAP key found for %Y to authenticate with AKA", id);
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, AKA_AK_LEN);
/* verify XMACS, AMF of zero is used in resynchronization */
memset(amf, 0, AKA_AMF_LEN);
this->f->f1star(this->f, k, rand, sqn, amf, xmacs);
if (!memeq(macs, xmacs, AKA_MAC_LEN))
{
DBG1(DBG_IKE, "received MACS does not match XMACS");
DBG3(DBG_IKE, "MACS %b XMACS %b",
macs, AKA_MAC_LEN, xmacs, AKA_MAC_LEN);
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;
}
/**
* Implementation of eap_aka_3gpp2_provider_t.destroy.
*/
static void destroy(private_eap_aka_3gpp2_provider_t *this)
{
free(this);
}
/**
* See header
*/
eap_aka_3gpp2_provider_t *eap_aka_3gpp2_provider_create(
eap_aka_3gpp2_functions_t *f)
{
private_eap_aka_3gpp2_provider_t *this = malloc_thing(private_eap_aka_3gpp2_provider_t);
this->public.provider.get_triplet = (bool(*)(sim_provider_t*, identification_t *id, char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN]))return_false;
this->public.provider.get_quintuplet = (bool(*)(sim_provider_t*, identification_t *id, char rand[AKA_RAND_LEN], char xres[AKA_RES_MAX], int *xres_len, char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], char autn[AKA_AUTN_LEN]))get_quintuplet;
this->public.provider.resync = (bool(*)(sim_provider_t*, identification_t *id, char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]))resync;
this->public.provider.is_pseudonym = (identification_t*(*)(sim_provider_t*, identification_t *id))return_null;
this->public.provider.gen_pseudonym = (identification_t*(*)(sim_provider_t*, identification_t *id))return_null;
this->public.provider.is_reauth = (identification_t*(*)(sim_provider_t*, identification_t *id, char [HASH_SIZE_SHA1], u_int16_t *counter))return_null;
this->public.provider.gen_reauth = (identification_t*(*)(sim_provider_t*, identification_t *id, char mk[HASH_SIZE_SHA1]))return_null;
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;
}
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2008-2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/**
* @defgroup eap_aka_3gpp2_provider eap_aka_3gpp2_provider
* @{ @ingroup eap_aka_3gpp2
*/
#ifndef EAP_AKA_3GPP2_PROVIDER_H_
#define EAP_AKA_3GPP2_PROVIDER_H_
#include "eap_aka_3gpp2_functions.h"
#include <sa/authenticators/eap/sim_manager.h>
typedef struct eap_aka_3gpp2_provider_t eap_aka_3gpp2_provider_t;
/**
* SIM provider implementation using a set of AKA functions.
*/
struct eap_aka_3gpp2_provider_t {
/**
* Implements sim_provider_t interface.
*/
sim_provider_t provider;
/**
* Destroy a eap_aka_3gpp2_provider_t.
*/
void (*destroy)(eap_aka_3gpp2_provider_t *this);
};
/**
* Create a eap_aka_3gpp2_provider instance.
*/
eap_aka_3gpp2_provider_t *eap_aka_3gpp2_provider_create(
eap_aka_3gpp2_functions_t *f);
#endif /** EAP_AKA_3GPP2_PROVIDER_H_ @}*/