Separated 3gpp2 USIM card and provider functionality
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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 {
|
||||
|
||||
|
||||
@@ -29,42 +29,109 @@ struct private_eap_aka_3gpp2_card_t {
|
||||
*/
|
||||
eap_aka_3gpp2_card_t public;
|
||||
|
||||
/**
|
||||
* IMSI, is ID_ANY for this software implementation
|
||||
*/
|
||||
identification_t *imsi;
|
||||
|
||||
/**
|
||||
* 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];
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -21,21 +21,14 @@
|
||||
#ifndef EAP_AKA_3GPP2_FUNCTIONS_H_
|
||||
#define EAP_AKA_3GPP2_FUNCTIONS_H_
|
||||
|
||||
#include <utils/enumerator.h>
|
||||
#include <utils/identification.h>
|
||||
#include <sa/authenticators/eap/usim_manager.h>
|
||||
|
||||
#define RAND_LENGTH 16
|
||||
#define RES_LENGTH 16
|
||||
#define SQN_LENGTH 6
|
||||
#define K_LENGTH 16
|
||||
#define MAC_LENGTH 8
|
||||
#define CK_LENGTH 16
|
||||
#define IK_LENGTH 16
|
||||
#define AK_LENGTH 6
|
||||
#define AMF_LENGTH 2
|
||||
#define FMK_LENGTH 4
|
||||
#define AUTN_LENGTH (SQN_LENGTH + AMF_LENGTH + MAC_LENGTH)
|
||||
#define AUTS_LENGTH (SQN_LENGTH + MAC_LENGTH)
|
||||
#define AKA_SQN_LEN 6
|
||||
#define AKA_K_LEN 16
|
||||
#define AKA_MAC_LEN 8
|
||||
#define AKA_AK_LEN 6
|
||||
#define AKA_AMF_LEN 2
|
||||
#define AKA_FMK_LEN 4
|
||||
|
||||
typedef struct eap_aka_3gpp2_functions_t eap_aka_3gpp2_functions_t;
|
||||
|
||||
@@ -53,9 +46,9 @@ struct eap_aka_3gpp2_functions_t {
|
||||
* @param amf authentication management field
|
||||
* @param mac buffer receiving mac MAC
|
||||
*/
|
||||
void (*f1)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH],
|
||||
u_char rand[RAND_LENGTH], u_char sqn[SQN_LENGTH],
|
||||
u_char amf[AMF_LENGTH], u_char mac[MAC_LENGTH]);
|
||||
void (*f1)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
|
||||
u_char rand[AKA_RAND_LEN], u_char sqn[AKA_SQN_LEN],
|
||||
u_char amf[AKA_AMF_LEN], u_char mac[AKA_MAC_LEN]);
|
||||
|
||||
/**
|
||||
* Calculate MACS from RAND, SQN, AMF using K
|
||||
@@ -66,9 +59,9 @@ struct eap_aka_3gpp2_functions_t {
|
||||
* @param amf authentication management field
|
||||
* @param macs buffer receiving resynchronization mac MACS
|
||||
*/
|
||||
void (*f1star)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH],
|
||||
u_char rand[RAND_LENGTH], u_char sqn[SQN_LENGTH],
|
||||
u_char amf[AMF_LENGTH], u_char macs[MAC_LENGTH]);
|
||||
void (*f1star)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
|
||||
u_char rand[AKA_RAND_LEN], u_char sqn[AKA_SQN_LEN],
|
||||
u_char amf[AKA_AMF_LEN], u_char macs[AKA_MAC_LEN]);
|
||||
|
||||
/**
|
||||
* Calculate RES from RAND using K
|
||||
@@ -77,8 +70,8 @@ struct eap_aka_3gpp2_functions_t {
|
||||
* @param rand random value RAND
|
||||
* @param macs buffer receiving result RES
|
||||
*/
|
||||
void (*f2)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH],
|
||||
u_char rand[RAND_LENGTH], u_char res[RES_LENGTH]);
|
||||
void (*f2)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
|
||||
u_char rand[AKA_RAND_LEN], u_char res[AKA_RES_LEN]);
|
||||
/**
|
||||
* Calculate CK from RAND using K
|
||||
*
|
||||
@@ -86,8 +79,8 @@ struct eap_aka_3gpp2_functions_t {
|
||||
* @param rand random value RAND
|
||||
* @param macs buffer receiving encryption key CK
|
||||
*/
|
||||
void (*f3)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH],
|
||||
u_char rand[RAND_LENGTH], u_char ck[CK_LENGTH]);
|
||||
void (*f3)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
|
||||
u_char rand[AKA_RAND_LEN], u_char ck[AKA_CK_LEN]);
|
||||
/**
|
||||
* Calculate IK from RAND using K
|
||||
*
|
||||
@@ -95,8 +88,8 @@ struct eap_aka_3gpp2_functions_t {
|
||||
* @param rand random value RAND
|
||||
* @param macs buffer receiving integrity key IK
|
||||
*/
|
||||
void (*f4)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH],
|
||||
u_char rand[RAND_LENGTH], u_char ik[IK_LENGTH]);
|
||||
void (*f4)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
|
||||
u_char rand[AKA_RAND_LEN], u_char ik[AKA_IK_LEN]);
|
||||
/**
|
||||
* Calculate AK from a RAND using K
|
||||
*
|
||||
@@ -104,8 +97,8 @@ struct eap_aka_3gpp2_functions_t {
|
||||
* @param rand random value RAND
|
||||
* @param macs buffer receiving anonymity key AK
|
||||
*/
|
||||
void (*f5)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH],
|
||||
u_char rand[RAND_LENGTH], u_char ak[AK_LENGTH]);
|
||||
void (*f5)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
|
||||
u_char rand[AKA_RAND_LEN], u_char ak[AKA_AK_LEN]);
|
||||
/**
|
||||
* Calculate AKS from a RAND using K
|
||||
*
|
||||
@@ -113,8 +106,8 @@ struct eap_aka_3gpp2_functions_t {
|
||||
* @param rand random value RAND
|
||||
* @param macs buffer receiving resynchronization anonymity key AKS
|
||||
*/
|
||||
void (*f5star)(eap_aka_3gpp2_functions_t *this, u_char k[K_LENGTH],
|
||||
u_char rand[RAND_LENGTH], u_char aks[AK_LENGTH]);
|
||||
void (*f5star)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
|
||||
u_char rand[AKA_RAND_LEN], u_char aks[AKA_AK_LEN]);
|
||||
|
||||
/**
|
||||
* Destroy a eap_aka_3gpp2_functions_t.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -65,19 +73,13 @@ 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]);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user