diff --git a/src/crypto.c b/src/crypto.c index a4a770f9..2e2e5362 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -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]; diff --git a/src/crypto.h b/src/crypto.h index 50299cb7..5e125dd0 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -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, diff --git a/src/eapol.c b/src/eapol.c index faa02991..13074cf7 100644 --- a/src/eapol.c +++ b/src/eapol.c @@ -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; diff --git a/src/ftutil.c b/src/ftutil.c index 04bf476d..e32d7e94 100644 --- a/src/ftutil.c +++ b/src/ftutil.c @@ -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; diff --git a/src/handshake.c b/src/handshake.c index 17fd0906..ac444685 100644 --- a/src/handshake.c +++ b/src/handshake.c @@ -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) diff --git a/src/handshake.h b/src/handshake.h index af3d2dd3..dd9c67e2 100644 --- a/src/handshake.h +++ b/src/handshake.h @@ -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,