From c6bb0eb32d6ef533b61d88bca31fe289c541f749 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 30 Aug 2017 14:00:21 -0700 Subject: [PATCH] simutil: Added new key/prf functions for EAP-AKA' This is the core key generation code for the AKA' method which follows RFC 5448. Two new functions are implemented, one for deriving CK'/IK' and the other for deriving the encryption keys using CK'/IK'. --- src/simutil.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/simutil.h | 45 +++++++++++++++++++++++ 2 files changed, 143 insertions(+) diff --git a/src/simutil.c b/src/simutil.c index 1c496db0..82cb61f3 100644 --- a/src/simutil.c +++ b/src/simutil.c @@ -270,6 +270,104 @@ bool eap_aka_get_milenage(const uint8_t *opc, const uint8_t *k, return true; } +bool eap_aka_derive_primes(const uint8_t *ck, const uint8_t *ik, + const uint8_t *autn, const uint8_t *network, uint16_t net_len, + uint8_t *ck_p, uint8_t *ik_p) +{ + struct iovec iov[5]; + struct l_checksum *hmac; + uint8_t key[32]; + uint8_t fc = 0x20; + uint16_t l1 = L_CPU_TO_BE16(6); + uint16_t name_len = L_CPU_TO_BE16(net_len); + uint8_t digest[32]; + + memcpy(key, ck, EAP_AKA_CK_LEN); + memcpy(key + EAP_AKA_CK_LEN, ik, EAP_AKA_IK_LEN); + + hmac = l_checksum_new_hmac(L_CHECKSUM_SHA256, key, 32); + if (!hmac) + return false; + + iov[0].iov_base = &fc; + iov[0].iov_len = 1; + iov[1].iov_base = (void *)network; + iov[1].iov_len = net_len; + iov[2].iov_base = &name_len; + iov[2].iov_len = 2; + iov[3].iov_base = (void *)autn; + iov[3].iov_len = 6; + iov[4].iov_base = &l1; + iov[4].iov_len = 2; + + l_checksum_updatev(hmac, iov, 5); + l_checksum_get_digest(hmac, digest, 32); + l_checksum_free(hmac); + + memcpy(ck_p, digest, EAP_AKA_CK_LEN); + memcpy(ik_p, digest + EAP_AKA_CK_LEN, EAP_AKA_IK_LEN); + + return true; +} + +bool eap_aka_prf_prime(const uint8_t *ik_p, const uint8_t *ck_p, + const char *identity, uint8_t *k_encr, uint8_t *k_aut, + uint8_t *k_re, uint8_t *msk, uint8_t *emsk) +{ + struct l_checksum *hmac; + uint8_t key[32]; + struct iovec iov[4]; + /* digest continues to be reused each iteration */ + uint8_t digest[32]; + uint8_t i = 0x01; + /* 7 iterations will be 224 bytes, 208 of which will get used */ + uint8_t out[224]; + uint8_t *pos = out; + + /* K = (IK'|CK') */ + memcpy(key, ik_p, EAP_AKA_IK_LEN); + memcpy(key + EAP_AKA_IK_LEN, ck_p, EAP_AKA_CK_LEN); + + iov[0].iov_base = digest; + /* initial iteration digest is not used */ + iov[0].iov_len = 0; + iov[1].iov_base = (void *)"EAP-AKA'"; + iov[1].iov_len = strlen("EAP-AKA'"); + iov[2].iov_base = (void *)identity; + iov[2].iov_len = strlen(identity); + iov[3].iov_base = &i; + iov[3].iov_len = 1; + + /* need 208 bytes for all keys */ + while (pos < out + 224) { + hmac = l_checksum_new_hmac(L_CHECKSUM_SHA256, key, 32); + if (!hmac) + return false; + + l_checksum_updatev(hmac, iov, 4); + l_checksum_get_digest(hmac, digest, 32); + l_checksum_free(hmac); + memcpy(pos, digest, 32); + pos += 32; + i++; + /* set the digest length so it can be prepended as Tn */ + iov[0].iov_len = 32; + } + + pos = out; + memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN); + pos += EAP_SIM_K_ENCR_LEN; + memcpy(k_aut, pos, EAP_AKA_PRIME_K_AUT_LEN); + pos += EAP_AKA_PRIME_K_AUT_LEN; + memcpy(k_re, pos, EAP_AKA_K_RE_LEN); + pos += EAP_AKA_K_RE_LEN; + memcpy(msk, pos, EAP_SIM_MSK_LEN); + pos += EAP_SIM_MSK_LEN; + memcpy(emsk, pos, EAP_SIM_EMSK_LEN); + + return true; +} + void eap_sim_fips_prf(const void *seed, size_t slen, uint8_t *out, size_t olen) { uint8_t xkey[64]; diff --git a/src/simutil.h b/src/simutil.h index bc1a0667..d8a063df 100644 --- a/src/simutil.h +++ b/src/simutil.h @@ -28,11 +28,15 @@ #define EAP_SIM_MK_LEN 20 #define EAP_SIM_K_ENCR_LEN 16 #define EAP_SIM_K_AUT_LEN 16 +#define EAP_AKA_PRIME_K_AUT_LEN 32 #define EAP_SIM_MSK_LEN 64 #define EAP_SIM_EMSK_LEN 64 #define EAP_SIM_IV_LEN 16 #define EAP_SIM_MAC_LEN 16 #define EAP_SIM_RAND_LEN 16 +#define EAP_AKA_K_RE_LEN 32 +#define EAP_AKA_IK_LEN 16 +#define EAP_AKA_CK_LEN 16 /* * Possible pad types for EAP-SIM/EAP-AKA attributes @@ -151,6 +155,47 @@ bool eap_aka_get_milenage(const uint8_t *opc, const uint8_t *k, const uint8_t *rand, const uint8_t *sqn, const uint8_t *amf, uint8_t *autn, uint8_t *ck, uint8_t *ik, uint8_t *res); +/* + * 3GPP TS 33.402 + * Derivation of CK' and IK' from CK and IK + * + * FC = 0x20 + * P0 = access network identity ('network') + * L0 = length of P0 + * P1 = SQN ^ AK = first 6 bytes of AUTN + * L1 = length of P1 = 0x0006 + * + * (CK' | IK') = HMAC_SHA256(FC |P0 | L0 | P1 | L1) + */ +bool eap_aka_derive_primes(const uint8_t *ck, const uint8_t *ik, + const uint8_t *autn, const uint8_t *network, uint16_t net_len, + uint8_t *ck_p, uint8_t *ik_p); + +/* + * RFC 5448, Section 3.4.1 + * + * PRF'(K,S) = T1 | T2 | T3 | T4 | ... + * + * where: + * 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) + * ... + * + * RFC 5448, Section 3.3 + * + * 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] + */ +bool eap_aka_prf_prime(const uint8_t *ik_p, const uint8_t *ck_p, + const char *identity, uint8_t *k_encr, uint8_t *k_aut, + uint8_t *k_re, uint8_t *msk, uint8_t *emsk); + /* * Separate PRNG data into encryption keys. k_encr and k_aut may be NULL in the * case of fast re-authentication.