aka-prime: EAP-AKA' implementation

This EAP method uses nearly all the logic from EAP-AKA. The major
difference is it uses the new key derivation functions for AKA' as
well as the SHA256 MAC calculation.
This commit is contained in:
James Prestwood 2017-08-30 16:22:45 -07:00 committed by Denis Kenzior
parent 83995b5099
commit 5d98c7adcf
2 changed files with 141 additions and 29 deletions

View File

@ -39,12 +39,12 @@
#define EAP_AKA_OPC_LEN 16
#define EAP_AKA_AMF_LEN 2
#define EAP_AKA_SQN_LEN 6
#define EAP_AKA_IK_LEN 16
#define EAP_AKA_CK_LEN 16
#define EAP_AKA_AUTN_LEN 16
#define EAP_AKA_RES_LEN 8
#define EAP_AKA_K_RE_LEN 32
#define EAP_AKA_KDF_DEFAULT 0x0001
#define EAP_AKA_ST_CHALLENGE 0x01
#define EAP_AKA_ST_AUTH_REJECT 0x02
#define EAP_AKA_ST_SYNC_FAILURE 0x04
@ -66,6 +66,7 @@ enum eap_aka_state {
struct eap_aka_handle {
enum eap_aka_state state;
enum eap_type type;
/* Identity from SIM */
char *identity;
@ -75,8 +76,8 @@ struct eap_aka_handle {
/* Derived K_encr key from PRNG */
uint8_t k_encr[EAP_SIM_K_ENCR_LEN];
/* Derived K_aut key from PRNG */
uint8_t k_aut[EAP_SIM_K_AUT_LEN];
/* Derived K_aut key from PRNG, extended for AKA' */
uint8_t k_aut[EAP_AKA_PRIME_K_AUT_LEN];
/* Derived MSK from PRNG */
uint8_t msk[EAP_SIM_MSK_LEN];
@ -113,6 +114,9 @@ struct eap_aka_handle {
/* Authentication value from AuC */
uint8_t autn[EAP_AKA_AUTN_LEN];
/* re-auth key */
uint8_t k_re[EAP_AKA_K_RE_LEN];
};
static int eap_aka_probe(struct eap_state *eap, const char *name)
@ -123,6 +127,22 @@ static int eap_aka_probe(struct eap_state *eap, const char *name)
return -ENOTSUP;
aka = l_new(struct eap_aka_handle, 1);
aka->type = EAP_TYPE_AKA;
eap_set_data(eap, aka);
return 0;
}
static int eap_aka_prime_probe(struct eap_state *eap, const char *name)
{
struct eap_aka_handle *aka;
if (strcasecmp(name, "AKA'"))
return -ENOTSUP;
aka = l_new(struct eap_aka_handle, 1);
aka->type = EAP_TYPE_AKA_PRIME;
eap_set_data(eap, aka);
@ -186,6 +206,11 @@ static void handle_challenge(struct eap_state *eap, const uint8_t *pkt,
uint8_t *pos = response;
const uint8_t *rand = NULL;
const uint8_t *autn = NULL;
bool kdf_func = false;
const uint8_t *kdf_in = NULL;
uint16_t kdf_in_len = 0;
uint8_t ik_p[EAP_AKA_IK_LEN];
uint8_t ck_p[EAP_AKA_CK_LEN];
if (len < 3) {
l_error("packet is too small");
@ -235,6 +260,48 @@ static void handle_challenge(struct eap_state *eap, const uint8_t *pkt,
break;
case EAP_SIM_AT_KDF:
if (aka->type != EAP_TYPE_AKA_PRIME) {
l_error("invalid attribute found for EAP-AKA");
goto chal_error;
}
if (length < 2) {
l_error("malformed AT_KDF");
goto chal_error;
}
if (l_get_be16(contents) != EAP_AKA_KDF_DEFAULT) {
l_error("KDF requested is not supported");
goto chal_error;
}
kdf_func = true;
break;
case EAP_SIM_AT_KDF_INPUT:
if (aka->type != EAP_TYPE_AKA_PRIME) {
l_error("invalid attribute found for EAP-AKA");
goto chal_error;
}
if (length < 3) {
l_error("malformed AT_KDF_INPUT");
goto chal_error;
}
kdf_in_len = l_get_be16(contents);
if (length < kdf_in_len + 2) {
l_error("malformed AT_KDF_INPUT");
goto chal_error;
}
kdf_in = contents + 2;
break;
case EAP_SIM_AT_NEXT_PSEUDONYM:
case EAP_SIM_AT_NEXT_REAUTH_ID:
case EAP_SIM_AT_IV:
@ -257,6 +324,11 @@ static void handle_challenge(struct eap_state *eap, const uint8_t *pkt,
}
}
if (aka->type == EAP_TYPE_AKA_PRIME && (!kdf_in || !kdf_func)) {
l_error("AT_KDF or AT_KDF_INPUT were not found");
goto chal_error;
}
eap_aka_get_milenage(aka->opc, aka->ki, rand, aka->sqn, aka->amf,
aka->autn, aka->ck, aka->ik, aka->res);
@ -265,20 +337,34 @@ static void handle_challenge(struct eap_state *eap, const uint8_t *pkt,
goto chal_error;
}
if (!derive_aka_mk(aka->identity, aka->ik, aka->ck, aka->mk)) {
l_error("error deriving MK");
goto chal_fatal;
if (aka->type == EAP_TYPE_AKA_PRIME) {
if (!eap_aka_derive_primes(aka->ck, aka->ik, aka->autn, kdf_in,
kdf_in_len, ck_p, ik_p)) {
l_error("could not derive primes");
goto chal_fatal;
}
if (!eap_aka_prf_prime(ik_p, ck_p, aka->identity, aka->k_encr,
aka->k_aut, aka->k_re, aka->msk, aka->emsk)) {
l_error("could not derive encryption keys");
goto chal_fatal;
}
} else {
if (!derive_aka_mk(aka->identity, aka->ik, aka->ck, aka->mk)) {
l_error("error deriving MK");
goto chal_fatal;
}
eap_sim_fips_prf(aka->mk, 20, prng_buf, 160);
if (!eap_sim_get_encryption_keys(prng_buf, aka->k_encr,
aka->k_aut, aka->msk, aka->emsk)) {
l_error("could not derive encryption keys");
goto chal_fatal;
}
}
eap_sim_fips_prf(aka->mk, 20, prng_buf, 160);
if (!eap_sim_get_encryption_keys(prng_buf, aka->k_encr, aka->k_aut,
aka->msk, aka->emsk)) {
l_error("could not derive encryption keys");
goto chal_fatal;
}
if (!eap_sim_verify_mac(eap, EAP_TYPE_AKA, pkt, len, aka->k_aut, NULL,
if (!eap_sim_verify_mac(eap, aka->type, pkt, len, aka->k_aut, NULL,
0)) {
l_error("MAC was not valid");
goto chal_error;
@ -286,7 +372,7 @@ static void handle_challenge(struct eap_state *eap, const uint8_t *pkt,
aka->state = EAP_AKA_STATE_CHALLENGE;
pos += eap_sim_build_header(eap, EAP_TYPE_AKA, EAP_AKA_ST_CHALLENGE,
pos += eap_sim_build_header(eap, aka->type, EAP_AKA_ST_CHALLENGE,
pos, resp_len);
pos += eap_sim_add_attribute(pos, EAP_SIM_AT_RES,
EAP_SIM_PAD_LENGTH_BITS, aka->res, EAP_AKA_RES_LEN);
@ -298,13 +384,13 @@ static void handle_challenge(struct eap_state *eap, const uint8_t *pkt,
pos += eap_sim_add_attribute(pos, EAP_SIM_AT_MAC, EAP_SIM_PAD_NONE,
NULL, EAP_SIM_MAC_LEN);
if (!eap_sim_derive_mac(EAP_TYPE_AKA, response, resp_len, aka->k_aut,
if (!eap_sim_derive_mac(aka->type, response, resp_len, aka->k_aut,
pos - EAP_SIM_MAC_LEN)) {
l_error("error deriving MAC");
goto chal_fatal;
}
eap_send_response(eap, EAP_TYPE_AKA, response, resp_len);
eap_send_response(eap, aka->type, response, resp_len);
if (!aka->protected) {
eap_method_success(eap);
@ -321,7 +407,7 @@ chal_fatal:
return;
chal_error:
eap_sim_client_error(eap, EAP_TYPE_AKA, EAP_SIM_ERROR_PROCESS);
eap_sim_client_error(eap, aka->type, EAP_SIM_ERROR_PROCESS);
}
/*
@ -384,12 +470,12 @@ static void handle_notification(struct eap_state *eap, const uint8_t *pkt,
/*
* Build response packet
*/
pos += eap_sim_build_header(eap, EAP_TYPE_AKA,
pos += eap_sim_build_header(eap, aka->type,
EAP_AKA_ST_NOTIFICATION, pos, 20);
pos += eap_sim_add_attribute(pos, EAP_SIM_AT_MAC,
EAP_SIM_PAD_NONE, NULL, EAP_SIM_MAC_LEN);
if (!eap_sim_derive_mac(EAP_TYPE_AKA, response, pos - response,
if (!eap_sim_derive_mac(aka->type, response, pos - response,
aka->k_aut, response + 12)) {
l_error("could not derive MAC");
eap_method_error(eap);
@ -397,7 +483,7 @@ static void handle_notification(struct eap_state *eap, const uint8_t *pkt,
return;
}
eap_send_response(eap, EAP_TYPE_AKA, response, pos - response);
eap_send_response(eap, aka->type, response, pos - response);
aka->state = EAP_AKA_STATE_SUCCESS;
@ -418,7 +504,7 @@ static void handle_notification(struct eap_state *eap, const uint8_t *pkt,
}
notif_error:
eap_sim_client_error(eap, EAP_TYPE_AKA, EAP_SIM_ERROR_PROCESS);
eap_sim_client_error(eap, aka->type, EAP_SIM_ERROR_PROCESS);
}
static void handle_identity(struct eap_state *eap, const uint8_t *pkt,
@ -430,7 +516,7 @@ static void handle_identity(struct eap_state *eap, const uint8_t *pkt,
if (aka->state != EAP_AKA_STATE_UNCONNECTED) {
l_error("invalid packet for EAP-AKA state");
eap_sim_client_error(eap, EAP_TYPE_AKA, EAP_SIM_ERROR_PROCESS);
eap_sim_client_error(eap, aka->type, EAP_SIM_ERROR_PROCESS);
return;
}
@ -438,18 +524,20 @@ static void handle_identity(struct eap_state *eap, const uint8_t *pkt,
/*
* Build response packet
*/
pos += eap_sim_build_header(eap, EAP_TYPE_AKA, EAP_AKA_ST_IDENTITY, pos,
pos += eap_sim_build_header(eap, aka->type, EAP_AKA_ST_IDENTITY, pos,
20);
pos += eap_sim_add_attribute(pos, EAP_SIM_AT_IDENTITY,
EAP_SIM_PAD_LENGTH, (uint8_t *)aka->identity,
strlen(aka->identity));
eap_send_response(eap, EAP_TYPE_AKA, response, pos - response);
eap_send_response(eap, aka->type, response, pos - response);
}
static void eap_aka_handle_request(struct eap_state *eap,
const uint8_t *pkt, size_t len)
{
struct eap_aka_handle *aka = eap_get_data(eap);
if (len < 1) {
l_error("packet is too small");
goto req_error;
@ -476,7 +564,7 @@ static void eap_aka_handle_request(struct eap_state *eap,
return;
req_error:
eap_sim_client_error(eap, EAP_TYPE_AKA, EAP_SIM_ERROR_PROCESS);
eap_sim_client_error(eap, aka->type, EAP_SIM_ERROR_PROCESS);
}
static bool eap_aka_load_settings(struct eap_state *eap,
@ -546,6 +634,16 @@ static struct eap_method eap_aka = {
.load_settings = eap_aka_load_settings,
};
static struct eap_method eap_aka_prime = {
.request_type = EAP_TYPE_AKA_PRIME,
.exports_msk = true,
.name = "AKA'",
.probe = eap_aka_prime_probe,
.remove = eap_aka_remove,
.handle_request = eap_aka_handle_request,
.load_settings = eap_aka_load_settings,
};
static int eap_aka_init(void)
{
l_debug("");
@ -558,4 +656,17 @@ static void eap_aka_exit(void)
eap_unregister_method(&eap_aka);
}
EAP_METHOD_BUILTIN(eap_aka, eap_aka_init, eap_aka_exit)
static int eap_aka_prime_init(void)
{
l_debug("");
return eap_register_method(&eap_aka_prime);
}
static void eap_aka_prime_exit(void)
{
l_debug("");
eap_unregister_method(&eap_aka_prime);
}
EAP_METHOD_BUILTIN(eap_aka, eap_aka_init, eap_aka_exit);
EAP_METHOD_BUILTIN(eap_aka_prime, eap_aka_prime_init, eap_aka_prime_exit);

View File

@ -86,6 +86,7 @@ enum eap_sim_at {
EAP_SIM_AT_NONCE_S = 0x15,
EAP_SIM_AT_CLIENT_ERROR_CODE = 0x16,
EAP_SIM_AT_KDF_INPUT = 0x17,
EAP_SIM_AT_KDF = 0x18,
EAP_SIM_AT_IV = 0x81,
EAP_SIM_AT_ENCR_DATA = 0x82,
EAP_SIM_AT_NEXT_PSEUDONYM = 0x84,