Separated 3gpp2 USIM card and provider functionality

This commit is contained in:
Martin Willi
2009-10-09 13:02:20 +02:00
parent 0030880c6b
commit 53a16b72ab
7 changed files with 445 additions and 855 deletions
File diff suppressed because it is too large Load Diff
+2 -23
View 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 {
@@ -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;
}
+19 -13
View 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]);
};
/**
@@ -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]);
};
/**