ap: Handle PTK handshake frame 2/4, send frame 3/4

This commit is contained in:
Andrew Zaborowski 2017-09-22 05:06:42 +02:00 committed by Denis Kenzior
parent c505de3582
commit a3361c5077
1 changed files with 113 additions and 0 deletions

113
src/ap.c
View File

@ -203,6 +203,111 @@ static void ap_send_ptk_1_of_4(struct ap_state *ap, struct sta_state *sta)
(struct eapol_frame *) ek);
}
/* 802.11-2016 Section 12.7.6.4 */
static void ap_send_ptk_3_of_4(struct ap_state *ap, struct sta_state *sta)
{
uint32_t ifindex = device_get_ifindex(ap->device);
uint8_t frame_buf[512];
uint8_t key_data_buf[128];
struct eapol_key *ek = (struct eapol_key *) frame_buf;
size_t key_data_len;
enum crypto_cipher cipher = ie_rsn_cipher_suite_to_cipher(ap->ciphers);
const struct crypto_ptk *ptk = (struct crypto_ptk *) sta->ptk;
struct ie_rsn_info rsn;
sta->key_replay_counter++;
memset(ek, 0, sizeof(struct eapol_key));
ek->header.protocol_version = EAPOL_PROTOCOL_VERSION_2004;
ek->header.packet_type = 0x3;
ek->descriptor_type = EAPOL_DESCRIPTOR_TYPE_80211;
/* Must be HMAC-SHA1-128 + AES when using CCMP with PSK or 8021X */
ek->key_descriptor_version = EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_SHA1_AES;
ek->key_type = true;
ek->install = true;
ek->key_ack = true;
ek->key_mic = true;
ek->secure = true;
ek->encrypted_key_data = true;
ek->key_length = L_CPU_TO_BE16(crypto_cipher_key_len(cipher));
ek->key_replay_counter = L_CPU_TO_BE64(sta->key_replay_counter);
memcpy(ek->key_nonce, sta->anonce, sizeof(ek->key_nonce));
/*
* We don't currently handle group traffic, to support that we'd need
* to provide the NL80211_ATTR_KEY_SEQ value from NL80211_CMD_GET_KEY
* here.
*/
l_put_be64(1, ek->key_rsc);
/*
* Just one RSNE in Key Data as we only set one cipher in ap->ciphers
* currently.
*/
ap_set_rsn_info(ap, &rsn);
if (!ie_build_rsne(&rsn, key_data_buf))
return;
if (!eapol_encrypt_key_data(ptk->kek, key_data_buf,
2 + key_data_buf[1], ek))
return;
key_data_len = L_BE16_TO_CPU(ek->key_data_len);
ek->header.packet_len = L_CPU_TO_BE16(sizeof(struct eapol_key) +
key_data_len - 4);
if (!eapol_calculate_mic(ptk->kck, ek, ek->key_mic_data))
return;
eapol_tx_frame(ifindex, ETH_P_PAE, sta->addr,
(struct eapol_frame *) ek);
}
/* 802.11-2016 Section 12.7.6.3 */
static void ap_handle_ptk_2_of_4(struct sta_state *sta,
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 *aa = device_get_address(sta->ap->device);
l_debug("");
if (!eapol_verify_ptk_2_of_4(ek))
return;
if (L_BE64_TO_CPU(ek->key_replay_counter) != sta->key_replay_counter)
return;
cipher = ie_rsn_cipher_suite_to_cipher(sta->ap->ciphers);
ptk_size = sizeof(struct crypto_ptk) + crypto_cipher_key_len(cipher);
if (!crypto_derive_pairwise_ptk(sta->ap->pmk, sta->addr, aa,
sta->anonce, ek->key_nonce,
ptk, ptk_size, false))
return;
if (!eapol_verify_mic(ptk->kck, ek))
return;
/* Bitwise identical RSNE required */
rsne = eapol_find_rsne(ek->key_data,
L_BE16_TO_CPU(ek->key_data_len), NULL);
if (!rsne || rsne[1] != sta->assoc_rsne_len ||
memcmp(rsne + 2, sta->assoc_rsne, rsne[1]))
/* TODO: Send Deauthenticate */
return;
memcpy(sta->ptk, ptk_buf, ptk_size);
memcpy(sta->snonce, ek->key_nonce, sizeof(sta->snonce));
sta->ptk_complete = true;
/* TODO: retry counters and timers */
ap_send_ptk_3_of_4(sta->ap, sta);
}
static void ap_eapol_key_handle(struct sta_state *sta,
const struct eapol_frame *frame)
{
@ -218,6 +323,9 @@ static void ap_eapol_key_handle(struct sta_state *sta,
if (!sta->have_anonce)
return; /* Not expecting an EAPoL-Key yet */
if (!sta->ptk_complete)
ap_handle_ptk_2_of_4(sta, ek);
}
static void ap_eapol_rx(uint16_t proto, const uint8_t *from,
@ -1099,6 +1207,11 @@ int ap_start(struct device *device, const char *ssid, const char *psk,
l_uintset_put(ap->rates, 2); /* 1 Mbps*/
l_uintset_put(ap->rates, 11); /* 5.5 Mbps*/
l_uintset_put(ap->rates, 22); /* 11 Mbps*/
if (crypto_psk_from_passphrase(psk, (uint8_t *) ssid, strlen(ssid),
ap->pmk) < 0)
goto error;
ap->frame_watch_ids = l_queue_new();
id = netdev_frame_watch_add(netdev, 0x0000 |