crypto/handshake/eapol: Allow other PTK lengths

The crypto_ptk was hard coded for 16 byte KCK/KEK. Depending on the
AKM these can be up to 32 bytes. This changes completely removes the
crypto_ptk struct and adds getters to the handshake object for the
kck and kek. Like before the PTK is derived into a continuous buffer,
and the kck/kek getters take care of returning the proper key offset
depending on AKM.

To allow for larger than 16 byte keys aes_unwrap needed to be
modified to take the kek length.
This commit is contained in:
James Prestwood 2019-01-17 12:25:30 -08:00 committed by Denis Kenzior
parent 3b801526f0
commit 6771a06463
6 changed files with 138 additions and 65 deletions

View File

@ -129,7 +129,7 @@ bool cmac_aes(const void *key, size_t key_len,
*
* NOTE: Buffers @in and @out can overlap
*/
bool aes_unwrap(const uint8_t *kek, const uint8_t *in, size_t len,
bool aes_unwrap(const uint8_t *kek, size_t kek_len, const uint8_t *in, size_t len,
uint8_t *out)
{
uint64_t b[2];
@ -139,7 +139,7 @@ bool aes_unwrap(const uint8_t *kek, const uint8_t *in, size_t len,
struct l_cipher *cipher;
uint64_t t = n * 6;
cipher = l_cipher_new(L_CIPHER_AES, kek, 16);
cipher = l_cipher_new(L_CIPHER_AES, kek, kek_len);
if (!cipher)
return false;
@ -552,7 +552,6 @@ static bool crypto_derive_ptk(const uint8_t *pmk, size_t pmk_len,
}
pos += 64;
if (use_sha256)
return kdf_sha256(pmk, pmk_len, label, strlen(label),
data, sizeof(data), out_ptk, ptk_len);
@ -564,12 +563,12 @@ static bool crypto_derive_ptk(const uint8_t *pmk, size_t pmk_len,
bool crypto_derive_pairwise_ptk(const uint8_t *pmk,
const uint8_t *addr1, const uint8_t *addr2,
const uint8_t *nonce1, const uint8_t *nonce2,
struct crypto_ptk *out_ptk, size_t ptk_len,
uint8_t *out_ptk, size_t ptk_len,
bool use_sha256)
{
return crypto_derive_ptk(pmk, 32, "Pairwise key expansion",
addr1, addr2, nonce1, nonce2,
(uint8_t *) out_ptk, ptk_len,
out_ptk, ptk_len,
use_sha256);
}
@ -677,7 +676,7 @@ exit:
bool crypto_derive_ft_ptk(const uint8_t *pmk_r1, const uint8_t *pmk_r1_name,
const uint8_t *addr1, const uint8_t *addr2,
const uint8_t *nonce1, const uint8_t *nonce2,
struct crypto_ptk *out_ptk, size_t ptk_len,
uint8_t *out_ptk, size_t ptk_len,
uint8_t *out_ptk_name)
{
uint8_t context[ETH_ALEN * 2 + 64];

View File

@ -54,12 +54,6 @@ enum crypto_akm {
#define CRYPTO_MIN_IGTK_LEN 16
#define CRYPTO_MAX_IGTK_LEN 32
struct crypto_ptk {
uint8_t kck[16];
uint8_t kek[16];
uint8_t tk[0];
} __attribute__ ((packed));
extern const unsigned char crypto_dh5_prime[];
extern size_t crypto_dh5_prime_size;
extern const unsigned char crypto_dh5_generator[];
@ -76,7 +70,7 @@ bool hmac_sha384(const void *key, size_t key_len,
bool cmac_aes(const void *key, size_t key_len,
const void *data, size_t data_len, void *output, size_t size);
bool aes_unwrap(const uint8_t *kek, const uint8_t *in, size_t len,
bool aes_unwrap(const uint8_t *kek, size_t kek_len, const uint8_t *in, size_t len,
uint8_t *out);
bool aes_wrap(const uint8_t *kek, const uint8_t *in, size_t len, uint8_t *out);
bool arc4_skip(const uint8_t *key, size_t key_len, size_t skip,
@ -104,7 +98,7 @@ bool hkdf_expand_sha256(const uint8_t *key, size_t key_len, const char *info,
bool crypto_derive_pairwise_ptk(const uint8_t *pmk,
const uint8_t *addr1, const uint8_t *addr2,
const uint8_t *nonce1, const uint8_t *nonce2,
struct crypto_ptk *out_ptk, size_t ptk_len,
uint8_t *out_ptk, size_t ptk_len,
bool use_sha256);
bool crypto_derive_pmk_r0(const uint8_t *xxkey,
@ -121,7 +115,7 @@ bool crypto_derive_pmk_r1(const uint8_t *pmk_r0,
bool crypto_derive_ft_ptk(const uint8_t *pmk_r1, const uint8_t *pmk_r1_name,
const uint8_t *addr1, const uint8_t *addr2,
const uint8_t *nonce1, const uint8_t *nonce2,
struct crypto_ptk *out_ptk, size_t ptk_len,
uint8_t *out_ptk, size_t ptk_len,
uint8_t *out_ptk_name);
bool crypto_derive_pmkid(const uint8_t *pmk,

View File

@ -226,7 +226,7 @@ uint8_t *eapol_decrypt_key_data(enum ie_rsn_akm_suite akm, const uint8_t *kek,
case EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_SHA1_AES:
case EAPOL_KEY_DESCRIPTOR_VERSION_AES_128_CMAC_AES:
case EAPOL_KEY_DESCRIPTOR_VERSION_AKM_DEFINED:
if (!aes_unwrap(kek, key_data, key_data_len, buf))
if (!aes_unwrap(kek, 16, key_data, key_data_len, buf))
goto error;
break;
@ -960,7 +960,7 @@ static void eapol_ptk_1_of_4_retry(struct l_timeout *timeout, void *user_data)
static void eapol_handle_ptk_1_of_4(struct eapol_sm *sm,
const struct eapol_key *ek)
{
const struct crypto_ptk *ptk;
const uint8_t *kck;
struct eapol_key *step2;
uint8_t mic[MIC_MAXLEN];
uint8_t *ies;
@ -1084,9 +1084,9 @@ static void eapol_handle_ptk_1_of_4(struct eapol_sm *sm,
sm->handshake->snonce, ies_len, ies,
sm->handshake->wpa_ie, sm->mic_len);
ptk = handshake_state_get_ptk(sm->handshake);
kck = handshake_state_get_kck(sm->handshake);
if (!eapol_calculate_mic(sm->handshake->akm_suite, ptk->kck,
if (!eapol_calculate_mic(sm->handshake->akm_suite, kck,
step2, mic, sm->mic_len)) {
l_info("MIC calculation failed. "
"Ensure Kernel Crypto is available.");
@ -1123,7 +1123,8 @@ static void eapol_send_ptk_3_of_4(struct eapol_sm *sm)
sm->handshake->pairwise_cipher);
enum crypto_cipher group_cipher = ie_rsn_cipher_suite_to_cipher(
sm->handshake->group_cipher);
const struct crypto_ptk *ptk = (struct crypto_ptk *) sm->handshake->ptk;
const uint8_t *kck;
const uint8_t *kek;
struct ie_rsn_info rsn;
uint8_t *rsne;
@ -1174,7 +1175,9 @@ static void eapol_send_ptk_3_of_4(struct eapol_sm *sm)
key_data_len += gtk_kde[1] + 2;
}
if (!eapol_encrypt_key_data(ptk->kek, key_data_buf,
kek = handshake_state_get_kek(sm->handshake);
if (!eapol_encrypt_key_data(kek, key_data_buf,
key_data_len, ek, sm->mic_len))
return;
@ -1183,7 +1186,9 @@ static void eapol_send_ptk_3_of_4(struct eapol_sm *sm)
ek->header.packet_len = L_CPU_TO_BE16(EAPOL_FRAME_LEN(sm->mic_len) +
key_data_len - 4);
if (!eapol_calculate_mic(sm->handshake->akm_suite, ptk->kck, ek,
kck = handshake_state_get_kck(sm->handshake);
if (!eapol_calculate_mic(sm->handshake->akm_suite, kck, ek,
EAPOL_KEY_MIC(ek), sm->mic_len))
return;
@ -1242,10 +1247,8 @@ static void eapol_handle_ptk_2_of_4(struct eapol_sm *sm,
const struct eapol_key *ek)
{
const uint8_t *rsne;
enum crypto_cipher cipher;
size_t ptk_size;
uint8_t ptk_buf[64];
struct crypto_ptk *ptk = (struct crypto_ptk *) ptk_buf;
const uint8_t *kck;
const uint8_t *aa = sm->handshake->aa;
l_debug("ifindex=%u", sm->handshake->ifindex);
@ -1256,15 +1259,17 @@ static void eapol_handle_ptk_2_of_4(struct eapol_sm *sm,
if (L_BE64_TO_CPU(ek->key_replay_counter) != sm->replay_counter)
return;
cipher = ie_rsn_cipher_suite_to_cipher(sm->handshake->pairwise_cipher);
ptk_size = sizeof(struct crypto_ptk) + crypto_cipher_key_len(cipher);
ptk_size = handshake_state_get_ptk_size(sm->handshake);
if (!crypto_derive_pairwise_ptk(sm->handshake->pmk, sm->handshake->spa,
aa, sm->handshake->anonce,
ek->key_nonce, ptk, ptk_size, false))
if (!crypto_derive_pairwise_ptk(sm->handshake->pmk,
sm->handshake->spa, aa,
sm->handshake->anonce, ek->key_nonce,
sm->handshake->ptk, ptk_size, false))
return;
if (!eapol_verify_mic(sm->handshake->akm_suite, ptk->kck, ek,
kck = handshake_state_get_kck(sm->handshake);
if (!eapol_verify_mic(sm->handshake->akm_suite, kck, ek,
sm->mic_len))
return;
@ -1282,7 +1287,6 @@ static void eapol_handle_ptk_2_of_4(struct eapol_sm *sm,
return;
}
memcpy(sm->handshake->ptk, ptk_buf, ptk_size);
memcpy(sm->handshake->snonce, ek->key_nonce,
sizeof(sm->handshake->snonce));
sm->handshake->have_snonce = true;
@ -1316,7 +1320,8 @@ static void eapol_handle_ptk_3_of_4(struct eapol_sm *sm,
const uint8_t *decrypted_key_data,
size_t decrypted_key_data_size)
{
const struct crypto_ptk *ptk;
const uint8_t *kck;
const uint8_t *kek;
struct eapol_key *step4;
uint8_t mic[MIC_MAXLEN];
const uint8_t *gtk = NULL;
@ -1516,9 +1521,10 @@ retransmit:
sm->replay_counter,
sm->handshake->wpa_ie, sm->mic_len);
ptk = handshake_state_get_ptk(sm->handshake);
kck = handshake_state_get_kck(sm->handshake);
kek = handshake_state_get_kek(sm->handshake);
if (!eapol_calculate_mic(sm->handshake->akm_suite, ptk->kck,
if (!eapol_calculate_mic(sm->handshake->akm_suite, kck,
step4, mic, sm->mic_len)) {
l_free(step4);
handshake_failed(sm, MMPDU_REASON_CODE_UNSPECIFIED);
@ -1550,7 +1556,7 @@ retransmit:
handshake_state_install_ptk(sm->handshake);
if (rekey_offload)
rekey_offload(sm->handshake->ifindex, ptk->kek, ptk->kck,
rekey_offload(sm->handshake->ifindex, kek, kck,
sm->replay_counter, sm->user_data);
l_timeout_remove(sm->timeout);
@ -1566,7 +1572,7 @@ error_ie_different:
static void eapol_handle_ptk_4_of_4(struct eapol_sm *sm,
const struct eapol_key *ek)
{
const struct crypto_ptk *ptk = (struct crypto_ptk *) sm->handshake->ptk;
const uint8_t *kck;
l_debug("ifindex=%u", sm->handshake->ifindex);
@ -1576,7 +1582,9 @@ static void eapol_handle_ptk_4_of_4(struct eapol_sm *sm,
if (L_BE64_TO_CPU(ek->key_replay_counter) != sm->replay_counter)
return;
if (!eapol_verify_mic(sm->handshake->akm_suite, ptk->kck, ek,
kck = handshake_state_get_kck(sm->handshake);
if (!eapol_verify_mic(sm->handshake->akm_suite, kck, ek,
sm->mic_len))
return;
@ -1591,7 +1599,7 @@ static void eapol_handle_gtk_1_of_2(struct eapol_sm *sm,
const uint8_t *decrypted_key_data,
size_t decrypted_key_data_size)
{
const struct crypto_ptk *ptk;
const uint8_t *kck;
struct eapol_key *step2;
uint8_t mic[MIC_MAXLEN];
const uint8_t *gtk;
@ -1664,9 +1672,9 @@ static void eapol_handle_gtk_1_of_2(struct eapol_sm *sm,
sm->handshake->wpa_ie, ek->wpa_key_id,
sm->mic_len);
ptk = handshake_state_get_ptk(sm->handshake);
kck = handshake_state_get_kck(sm->handshake);
if (!eapol_calculate_mic(sm->handshake->akm_suite, ptk->kck,
if (!eapol_calculate_mic(sm->handshake->akm_suite, kck,
step2, mic, sm->mic_len)) {
l_free(step2);
handshake_failed(sm, MMPDU_REASON_CODE_UNSPECIFIED);
@ -1708,7 +1716,8 @@ static void eapol_key_handle(struct eapol_sm *sm,
const struct eapol_frame *frame)
{
const struct eapol_key *ek;
const struct crypto_ptk *ptk;
const uint8_t *kck;
const uint8_t *kek;
uint8_t *decrypted_key_data = NULL;
size_t key_data_len = 0;
uint64_t replay_counter;
@ -1761,14 +1770,14 @@ static void eapol_key_handle(struct eapol_sm *sm,
if (sm->have_replay && sm->replay_counter >= replay_counter)
return;
ptk = handshake_state_get_ptk(sm->handshake);
kck = handshake_state_get_kck(sm->handshake);
if (ek->key_mic) {
/* Haven't received step 1 yet, so no ptk */
if (!sm->handshake->have_snonce)
return;
if (!eapol_verify_mic(sm->handshake->akm_suite, ptk->kck, ek,
if (!eapol_verify_mic(sm->handshake->akm_suite, kck, ek,
sm->mic_len))
return;
}
@ -1779,8 +1788,10 @@ static void eapol_key_handle(struct eapol_sm *sm,
if (!sm->handshake->have_snonce)
return;
kek = handshake_state_get_kek(sm->handshake);
decrypted_key_data = eapol_decrypt_key_data(
sm->handshake->akm_suite, ptk->kek,
sm->handshake->akm_suite, kek,
ek, &key_data_len, sm->mic_len);
if (!decrypted_key_data)
return;

View File

@ -44,7 +44,7 @@ bool ft_calculate_fte_mic(struct handshake_state *hs, uint8_t seq_num,
struct iovec iov[10];
int iov_elems = 0;
struct l_checksum *checksum;
const struct crypto_ptk *ptk = handshake_state_get_ptk(hs);
const uint8_t *kck = handshake_state_get_kck(hs);
uint8_t zero_mic[16] = {};
iov[iov_elems].iov_base = hs->spa;
@ -80,7 +80,7 @@ bool ft_calculate_fte_mic(struct handshake_state *hs, uint8_t seq_num,
iov[iov_elems++].iov_len = ric[1] + 2;
}
checksum = l_checksum_new_cmac_aes(ptk->kck, 16);
checksum = l_checksum_new_cmac_aes(kck, 16);
if (!checksum)
return false;

View File

@ -278,10 +278,51 @@ void handshake_state_set_anonce(struct handshake_state *s,
memcpy(s->anonce, anonce, 32);
}
/* A multi-purpose getter for key sizes */
static bool handshake_get_key_sizes(struct handshake_state *s, size_t *ptk_size,
size_t *kck_size, size_t *kek_size)
{
size_t kck;
size_t kek;
size_t tk;
enum crypto_cipher cipher =
ie_rsn_cipher_suite_to_cipher(s->pairwise_cipher);
tk = crypto_cipher_key_len(cipher);
/*
* IEEE 802.11-2016 Table 12-8: Integrity and key-wrap algorithms
*
* From the table, only 00-0F-AC:12 and 00-0F-AC:13 use longer KCK and
* KEK keys, which are 24 and 32 bytes respectively. The remainder use
* 16 and 16 respectively.
*/
switch (s->akm_suite) {
case IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA256:
case IE_RSN_AKM_SUITE_FT_OVER_8021X_SHA384:
kck = 24;
kek = 32;
break;
default:
kck = 16;
kek = 16;
break;
}
if (ptk_size)
*ptk_size = kck + kek + tk;
if (kck_size)
*kck_size = kck;
if (kek_size)
*kek_size = kek;
return true;
}
bool handshake_state_derive_ptk(struct handshake_state *s)
{
struct crypto_ptk *ptk = (struct crypto_ptk *) s->ptk;
enum crypto_cipher cipher;
size_t ptk_size;
bool use_sha256;
@ -305,9 +346,7 @@ bool handshake_state_derive_ptk(struct handshake_state *s)
else
use_sha256 = false;
cipher = ie_rsn_cipher_suite_to_cipher(s->pairwise_cipher);
ptk_size = sizeof(struct crypto_ptk) + crypto_cipher_key_len(cipher);
ptk_size = handshake_state_get_ptk_size(s);
if (s->akm_suite & (IE_RSN_AKM_SUITE_FT_OVER_8021X |
IE_RSN_AKM_SUITE_FT_USING_PSK |
@ -344,26 +383,54 @@ bool handshake_state_derive_ptk(struct handshake_state *s)
if (!crypto_derive_ft_ptk(s->pmk_r1, s->pmk_r1_name, s->aa,
s->spa, s->snonce, s->anonce,
ptk, ptk_size, ptk_name))
s->ptk, ptk_size, ptk_name))
return false;
} else
if (!crypto_derive_pairwise_ptk(s->pmk, s->spa, s->aa,
s->anonce, s->snonce,
ptk, ptk_size, use_sha256))
if (!crypto_derive_pairwise_ptk(s->pmk, s->spa,
s->aa, s->anonce, s->snonce,
s->ptk, ptk_size, use_sha256))
return false;
return true;
}
const struct crypto_ptk *handshake_state_get_ptk(struct handshake_state *s)
size_t handshake_state_get_ptk_size(struct handshake_state *s)
{
return (struct crypto_ptk *) s->ptk;
size_t ptk_size;
if (!handshake_get_key_sizes(s, &ptk_size, NULL, NULL))
return 0;
return ptk_size;
}
const uint8_t *handshake_state_get_kck(struct handshake_state *s)
{
return s->ptk;
}
const uint8_t *handshake_state_get_kek(struct handshake_state *s)
{
size_t kck_size;
if (!handshake_get_key_sizes(s, NULL, &kck_size, NULL))
return NULL;
return s->ptk + kck_size;
}
static const uint8_t *handshake_get_tk(struct handshake_state *s)
{
size_t kck_size, kek_size;
if (!handshake_get_key_sizes(s, NULL, &kck_size, &kek_size))
return NULL;
return s->ptk + kck_size + kek_size;
}
void handshake_state_install_ptk(struct handshake_state *s)
{
struct crypto_ptk *ptk = (struct crypto_ptk *) s->ptk;
s->ptk_complete = true;
if (install_tk) {
@ -372,7 +439,7 @@ void handshake_state_install_ptk(struct handshake_state *s)
handshake_event(s, HANDSHAKE_EVENT_SETTING_KEYS, NULL);
install_tk(s, ptk->tk, cipher);
install_tk(s, handshake_get_tk(s), cipher);
}
}
@ -684,10 +751,10 @@ void handshake_util_build_gtk_kde(enum crypto_cipher cipher, const uint8_t *key,
bool handshake_decode_fte_key(struct handshake_state *s, const uint8_t *wrapped,
size_t key_len, uint8_t *key_out)
{
const struct crypto_ptk *ptk = handshake_state_get_ptk(s);
const uint8_t *kek = handshake_state_get_kek(s);
size_t padded_len = key_len < 16 ? 16 : align_len(key_len, 8);
if (!aes_unwrap(ptk->kek, wrapped, padded_len + 8, key_out))
if (!aes_unwrap(kek, 16, wrapped, padded_len + 8, key_out))
return false;
if (key_len < padded_len && key_out[key_len++] != 0xdd)

View File

@ -90,7 +90,7 @@ struct handshake_state {
size_t pmk_len;
uint8_t snonce[32];
uint8_t anonce[32];
uint8_t ptk[64];
uint8_t ptk[80];
uint8_t pmk_r0[32];
uint8_t pmk_r0_name[16];
uint8_t pmk_r1[32];
@ -164,7 +164,9 @@ void handshake_state_set_anonce(struct handshake_state *s,
const uint8_t *anonce);
void handshake_state_set_pmkid(struct handshake_state *s, const uint8_t *pmkid);
bool handshake_state_derive_ptk(struct handshake_state *s);
const struct crypto_ptk *handshake_state_get_ptk(struct handshake_state *s);
size_t handshake_state_get_ptk_size(struct handshake_state *s);
const uint8_t *handshake_state_get_kck(struct handshake_state *s);
const uint8_t *handshake_state_get_kek(struct handshake_state *s);
void handshake_state_install_ptk(struct handshake_state *s);
void handshake_state_install_gtk(struct handshake_state *s,