EAP-AKA': Derive keys using the new KDF (PRF')

This commit is contained in:
Jouni Malinen 2008-12-03 19:22:20 +02:00
parent f07688e37d
commit 9881795e2c
6 changed files with 320 additions and 51 deletions

View file

@ -234,6 +234,134 @@ void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac,
#ifdef EAP_AKA_PRIME
static void prf_prime(const u8 *k, const char *seed1,
const u8 *seed2, size_t seed2_len,
const u8 *seed3, size_t seed3_len,
u8 *res, size_t res_len)
{
const u8 *addr[5];
size_t len[5];
u8 hash[SHA256_MAC_LEN];
u8 iter;
/*
* PRF'(K,S) = T1 | T2 | T3 | T4 | ...
* T1 = HMAC-SHA-256 (K, S | 0x01)
* T2 = HMAC-SHA-256 (K, T1 | S | 0x02)
* T3 = HMAC-SHA-256 (K, T2 | S | 0x03)
* T4 = HMAC-SHA-256 (K, T3 | S | 0x04)
* ...
*/
addr[0] = hash;
len[0] = 0;
addr[1] = (const u8 *) seed1;
len[1] = os_strlen(seed1);
addr[2] = seed2;
len[2] = seed2_len;
addr[3] = seed3;
len[3] = seed3_len;
addr[4] = &iter;
len[4] = 1;
iter = 0;
while (res_len) {
size_t hlen;
iter++;
hmac_sha256_vector(k, 32, 5, addr, len, hash);
len[0] = SHA256_MAC_LEN;
hlen = res_len > SHA256_MAC_LEN ? SHA256_MAC_LEN : res_len;
os_memcpy(res, hash, hlen);
res += hlen;
res_len -= hlen;
}
}
void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len,
const u8 *ik, const u8 *ck, u8 *k_encr,
u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk)
{
u8 key[EAP_AKA_IK_LEN + EAP_AKA_CK_LEN];
u8 keys[EAP_SIM_K_ENCR_LEN + EAP_AKA_PRIME_K_AUT_LEN +
EAP_AKA_PRIME_K_RE_LEN + EAP_MSK_LEN + EAP_EMSK_LEN];
u8 *pos;
/*
* MK = PRF'(IK'|CK',"EAP-AKA'"|Identity)
* K_encr = MK[0..127]
* K_aut = MK[128..383]
* K_re = MK[384..639]
* MSK = MK[640..1151]
* EMSK = MK[1152..1663]
*/
os_memcpy(key, ik, EAP_AKA_IK_LEN);
os_memcpy(key + EAP_AKA_IK_LEN, ck, EAP_AKA_CK_LEN);
prf_prime(key, "EAP-AKA'", identity, identity_len, NULL, 0,
keys, sizeof(keys));
pos = keys;
os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_encr",
k_encr, EAP_SIM_K_ENCR_LEN);
pos += EAP_SIM_K_ENCR_LEN;
os_memcpy(k_aut, pos, EAP_AKA_PRIME_K_AUT_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_aut",
k_aut, EAP_AKA_PRIME_K_AUT_LEN);
pos += EAP_AKA_PRIME_K_AUT_LEN;
os_memcpy(k_re, pos, EAP_AKA_PRIME_K_RE_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_re",
k_re, EAP_AKA_PRIME_K_RE_LEN);
pos += EAP_AKA_PRIME_K_RE_LEN;
os_memcpy(msk, pos, EAP_MSK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN);
pos += EAP_MSK_LEN;
os_memcpy(emsk, pos, EAP_EMSK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN);
}
int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
const u8 *identity, size_t identity_len,
const u8 *nonce_s, u8 *msk, u8 *emsk)
{
u8 seed3[2 + EAP_SIM_NONCE_S_LEN];
u8 keys[EAP_MSK_LEN + EAP_EMSK_LEN];
u8 *pos;
/*
* MK = PRF'(K_re,"EAP-AKA' re-auth"|Identity|counter|NONCE_S)
* MSK = MK[0..511]
* EMSK = MK[512..1023]
*/
WPA_PUT_BE16(seed3, counter);
os_memcpy(seed3 + 2, nonce_s, EAP_SIM_NONCE_S_LEN);
prf_prime(k_re, "EAP-AKA' re-auth", identity, identity_len,
seed3, sizeof(seed3),
keys, sizeof(keys));
pos = keys;
os_memcpy(msk, pos, EAP_MSK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN);
pos += EAP_MSK_LEN;
os_memcpy(emsk, pos, EAP_EMSK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN);
os_memset(keys, 0, sizeof(keys));
return 0;
}
int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req,
const u8 *mac, const u8 *extra, size_t extra_len)
{

View file

@ -73,6 +73,7 @@
#define EAP_AKA_PRIME_K_AUT_LEN 32
#define EAP_AKA_PRIME_CHECKCODE_LEN 32
#define EAP_AKA_PRIME_K_RE_LEN 32
struct wpabuf;
@ -92,6 +93,13 @@ int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req,
const u8 *mac, const u8 *extra, size_t extra_len);
void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac,
const u8 *extra, size_t extra_len);
void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len,
const u8 *ik, const u8 *ck, u8 *k_encr,
u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk);
int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
const u8 *identity, size_t identity_len,
const u8 *nonce_s, u8 *msk, u8 *emsk);
int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req,
const u8 *mac, const u8 *extra,
size_t extra_len);

View file

@ -34,6 +34,7 @@ struct eap_aka_data {
u8 mk[EAP_SIM_MK_LEN];
u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
u8 k_encr[EAP_SIM_K_ENCR_LEN];
u8 k_re[EAP_AKA_PRIME_K_RE_LEN];
u8 msk[EAP_SIM_KEYING_DATA_LEN];
u8 emsk[EAP_EMSK_LEN];
u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN];
@ -701,10 +702,16 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
identity = eap_get_config_identity(sm, &identity_len);
wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK "
"derivation", identity, identity_len);
if (data->eap_method == EAP_TYPE_AKA_PRIME) {
eap_aka_prime_derive_keys(identity, identity_len, data->ik,
data->ck, data->k_encr, data->k_aut,
data->k_re, data->msk, data->emsk);
} else {
eap_aka_derive_mk(identity, identity_len, data->ik, data->ck,
data->mk);
eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
data->emsk);
eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
data->msk, data->emsk);
}
if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
"used invalid AT_MAC");
@ -910,10 +917,6 @@ static struct wpabuf * eap_aka_process_reauthentication(
"(%d <= %d)", eattr.counter, data->counter);
data->counter_too_small = eattr.counter;
eap_sim_derive_keys_reauth(eattr.counter, data->reauth_id,
data->reauth_id_len, eattr.nonce_s,
data->mk, NULL, NULL);
/* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current
* reauth_id must not be used to start a new reauthentication.
* However, since it was used in the last EAP-Response-Identity
@ -936,10 +939,18 @@ static struct wpabuf * eap_aka_process_reauthentication(
wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S",
data->nonce_s, EAP_SIM_NONCE_S_LEN);
eap_sim_derive_keys_reauth(data->counter,
data->reauth_id, data->reauth_id_len,
data->nonce_s, data->mk, data->msk,
data->emsk);
if (data->eap_method == EAP_TYPE_AKA_PRIME) {
eap_aka_prime_derive_keys_reauth(data->k_re, data->counter,
data->reauth_id,
data->reauth_id_len,
data->nonce_s,
data->msk, data->emsk);
} else {
eap_sim_derive_keys_reauth(data->counter, data->reauth_id,
data->reauth_id_len,
data->nonce_s, data->mk,
data->msk, data->emsk);
}
eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
eap_aka_learn_ids(data, &eattr);

View file

@ -28,6 +28,7 @@ struct eap_aka_data {
u8 nonce_s[EAP_SIM_NONCE_S_LEN];
u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
u8 k_encr[EAP_SIM_K_ENCR_LEN];
u8 k_re[EAP_AKA_PRIME_K_RE_LEN];
u8 msk[EAP_SIM_KEYING_DATA_LEN];
u8 emsk[EAP_EMSK_LEN];
u8 rand[EAP_AKA_RAND_LEN];
@ -365,11 +366,19 @@ static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm,
wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S",
data->nonce_s, EAP_SIM_NONCE_S_LEN);
eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
data->emsk);
eap_sim_derive_keys_reauth(data->counter, sm->identity,
sm->identity_len, data->nonce_s, data->mk,
if (data->eap_method == EAP_TYPE_AKA_PRIME) {
eap_aka_prime_derive_keys_reauth(data->k_re, data->counter,
sm->identity,
sm->identity_len,
data->nonce_s,
data->msk, data->emsk);
} else {
eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
data->msk, data->emsk);
eap_sim_derive_keys_reauth(data->counter, sm->identity,
sm->identity_len, data->nonce_s,
data->mk, data->msk, data->emsk);
}
msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
EAP_AKA_SUBTYPE_REAUTHENTICATION);
@ -545,17 +554,36 @@ static void eap_aka_determine_identity(struct eap_sm *sm,
data->reauth = eap_sim_db_get_reauth_entry(
sm->eap_sim_db_priv, sm->identity,
sm->identity_len);
if (data->reauth &&
data->reauth->aka_prime !=
(data->eap_method == EAP_TYPE_AKA_PRIME)) {
wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth data "
"was for different AKA version");
data->reauth = NULL;
}
if (data->reauth) {
wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast "
"re-authentication");
identity = data->reauth->identity;
identity_len = data->reauth->identity_len;
data->counter = data->reauth->counter;
if (data->eap_method == EAP_TYPE_AKA_PRIME) {
os_memcpy(data->k_encr,
data->reauth->k_encr,
EAP_SIM_K_ENCR_LEN);
os_memcpy(data->k_aut,
data->reauth->k_aut,
EAP_AKA_PRIME_K_AUT_LEN);
os_memcpy(data->k_re,
data->reauth->k_re,
EAP_AKA_PRIME_K_RE_LEN);
} else {
os_memcpy(data->mk, data->reauth->mk,
EAP_SIM_MK_LEN);
}
}
}
}
if (identity == NULL ||
eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
@ -618,10 +646,16 @@ static void eap_aka_determine_identity(struct eap_sm *sm,
wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity for MK derivation",
sm->identity, identity_len);
eap_aka_derive_mk(sm->identity, identity_len, data->ik, data->ck,
data->mk);
eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
data->emsk);
if (data->eap_method == EAP_TYPE_AKA_PRIME) {
eap_aka_prime_derive_keys(identity, identity_len, data->ik,
data->ck, data->k_encr, data->k_aut,
data->k_re, data->msk, data->emsk);
} else {
eap_aka_derive_mk(sm->identity, identity_len, data->ik,
data->ck, data->mk);
eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
data->msk, data->emsk);
}
eap_aka_state(data, CHALLENGE);
}
@ -741,10 +775,21 @@ static void eap_aka_process_challenge(struct eap_sm *sm,
data->next_pseudonym = NULL;
}
if (data->next_reauth_id) {
if (data->eap_method == EAP_TYPE_AKA_PRIME) {
eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
identity,
identity_len,
data->next_reauth_id,
data->counter + 1,
data->k_encr, data->k_aut,
data->k_re);
} else {
eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
identity_len,
data->next_reauth_id, data->counter + 1,
data->next_reauth_id,
data->counter + 1,
data->mk);
}
data->next_reauth_id = NULL;
}
}
@ -867,9 +912,21 @@ static void eap_aka_process_reauth(struct eap_sm *sm,
data->next_pseudonym = NULL;
}
if (data->next_reauth_id) {
if (data->eap_method == EAP_TYPE_AKA_PRIME) {
eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
identity,
identity_len,
data->next_reauth_id,
data->counter + 1,
data->k_encr, data->k_aut,
data->k_re);
} else {
eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
identity_len, data->next_reauth_id,
data->counter + 1, data->mk);
identity_len,
data->next_reauth_id,
data->counter + 1,
data->mk);
}
data->next_reauth_id = NULL;
} else {
eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);

View file

@ -942,27 +942,12 @@ int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
}
/**
* eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
* @priv: Private data pointer from eap_sim_db_init()
* @identity: Identity of the user (may be permanent identity or pseudonym)
* @identity_len: Length of identity
* @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
* e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
* free it.
* @mk: 16-byte MK from the previous full authentication
* Returns: 0 on success, -1 on failure
*
* This function adds a new re-authentication entry for an EAP-SIM user.
* EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
* anymore.
*/
int eap_sim_db_add_reauth(void *priv, const u8 *identity,
size_t identity_len, char *reauth_id, u16 counter,
const u8 *mk)
static struct eap_sim_reauth *
eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
size_t identity_len, char *reauth_id, u16 counter)
{
struct eap_sim_db_data *data = priv;
struct eap_sim_reauth *r;
wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity",
identity, identity_len);
wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id);
@ -980,7 +965,7 @@ int eap_sim_db_add_reauth(void *priv, const u8 *identity,
r = os_zalloc(sizeof(*r));
if (r == NULL) {
os_free(reauth_id);
return -1;
return NULL;
}
r->next = data->reauths;
@ -988,7 +973,7 @@ int eap_sim_db_add_reauth(void *priv, const u8 *identity,
if (r->identity == NULL) {
os_free(r);
os_free(reauth_id);
return -1;
return NULL;
}
os_memcpy(r->identity, identity, identity_len);
r->identity_len = identity_len;
@ -998,12 +983,84 @@ int eap_sim_db_add_reauth(void *priv, const u8 *identity,
}
r->counter = counter;
return r;
}
/**
* eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
* @priv: Private data pointer from eap_sim_db_init()
* @identity: Identity of the user (may be permanent identity or pseudonym)
* @identity_len: Length of identity
* @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
* e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
* free it.
* @mk: 16-byte MK from the previous full authentication or %NULL
* Returns: 0 on success, -1 on failure
*
* This function adds a new re-authentication entry for an EAP-SIM user.
* EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
* anymore.
*/
int eap_sim_db_add_reauth(void *priv, const u8 *identity,
size_t identity_len, char *reauth_id, u16 counter,
const u8 *mk)
{
struct eap_sim_db_data *data = priv;
struct eap_sim_reauth *r;
r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
counter);
if (r == NULL)
return -1;
os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
r->aka_prime = 0;
return 0;
}
#ifdef EAP_AKA_PRIME
/**
* eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry
* @priv: Private data pointer from eap_sim_db_init()
* @identity: Identity of the user (may be permanent identity or pseudonym)
* @identity_len: Length of identity
* @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
* e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
* free it.
* @k_re: 32-byte K_re from the previous full authentication or %NULL
* Returns: 0 on success, -1 on failure
*
* This function adds a new re-authentication entry for an EAP-AKA' user.
* EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
* anymore.
*/
int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
size_t identity_len, char *reauth_id,
u16 counter, const u8 *k_encr, const u8 *k_aut,
const u8 *k_re)
{
struct eap_sim_db_data *data = priv;
struct eap_sim_reauth *r;
r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
counter);
if (r == NULL)
return -1;
r->aka_prime = 1;
os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN);
os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN);
os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN);
return 0;
}
#endif /* EAP_AKA_PRIME */
/**
* eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
* @priv: Private data pointer from eap_sim_db_init()

View file

@ -54,6 +54,10 @@ int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
int eap_sim_db_add_reauth(void *priv, const u8 *identity,
size_t identity_len, char *reauth_id, u16 counter,
const u8 *mk);
int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
size_t identity_len, char *reauth_id,
u16 counter, const u8 *k_encr, const u8 *k_aut,
const u8 *k_re);
const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
size_t identity_len, size_t *len);
@ -64,7 +68,11 @@ struct eap_sim_reauth {
size_t identity_len;
char *reauth_id;
u16 counter;
int aka_prime;
u8 mk[EAP_SIM_MK_LEN];
u8 k_encr[EAP_SIM_K_ENCR_LEN];
u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
u8 k_re[EAP_AKA_PRIME_K_RE_LEN];
};
struct eap_sim_reauth *