wlantest: TKIP frame reassembly for Michael MIC check in fragmented case

Reassemble the full MSDU when processing TKIP protected fragmented
frames so that the Michael MIC can be validated once the last fragment
has been received.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
This commit is contained in:
Jouni Malinen 2021-02-08 18:38:39 +02:00 committed by Jouni Malinen
parent 3332657d69
commit ced15c8ba8
5 changed files with 72 additions and 15 deletions

View file

@ -177,7 +177,8 @@ static u8 * try_ptk(struct wlantest *wt, int pairwise_cipher,
enum michael_mic_result mic_res; enum michael_mic_result mic_res;
decrypted = tkip_decrypt(ptk->tk, hdr, data, data_len, decrypted = tkip_decrypt(ptk->tk, hdr, data, data_len,
decrypted_len, &mic_res); decrypted_len, &mic_res,
&wt->tkip_frag);
if (decrypted && mic_res == MICHAEL_MIC_INCORRECT) if (decrypted && mic_res == MICHAEL_MIC_INCORRECT)
add_note(wt, MSG_INFO, "Invalid Michael MIC"); add_note(wt, MSG_INFO, "Invalid Michael MIC");
else if (decrypted && mic_res == MICHAEL_MIC_NOT_VERIFIED) else if (decrypted && mic_res == MICHAEL_MIC_NOT_VERIFIED)
@ -328,7 +329,7 @@ skip_replay_det:
enum michael_mic_result mic_res; enum michael_mic_result mic_res;
decrypted = tkip_decrypt(bss->gtk[keyid], hdr, data, len, decrypted = tkip_decrypt(bss->gtk[keyid], hdr, data, len,
&dlen, &mic_res); &dlen, &mic_res, &wt->tkip_frag);
if (decrypted && mic_res == MICHAEL_MIC_INCORRECT) if (decrypted && mic_res == MICHAEL_MIC_INCORRECT)
add_note(wt, MSG_INFO, "Invalid Michael MIC"); add_note(wt, MSG_INFO, "Invalid Michael MIC");
else if (decrypted && mic_res == MICHAEL_MIC_NOT_VERIFIED) else if (decrypted && mic_res == MICHAEL_MIC_NOT_VERIFIED)
@ -619,7 +620,7 @@ skip_replay_det:
enum michael_mic_result mic_res; enum michael_mic_result mic_res;
decrypted = tkip_decrypt(sta->ptk.tk, hdr, data, len, &dlen, decrypted = tkip_decrypt(sta->ptk.tk, hdr, data, len, &dlen,
&mic_res); &mic_res, &wt->tkip_frag);
if (decrypted && mic_res == MICHAEL_MIC_INCORRECT) if (decrypted && mic_res == MICHAEL_MIC_INCORRECT)
add_note(wt, MSG_INFO, "Invalid Michael MIC"); add_note(wt, MSG_INFO, "Invalid Michael MIC");
else if (decrypted && mic_res == MICHAEL_MIC_NOT_VERIFIED) else if (decrypted && mic_res == MICHAEL_MIC_NOT_VERIFIED)

View file

@ -63,7 +63,7 @@ static void test_vector_tkip(void)
wpa_debug_level = MSG_INFO; wpa_debug_level = MSG_INFO;
plain = tkip_decrypt(tk, (const struct ieee80211_hdr *) enc, plain = tkip_decrypt(tk, (const struct ieee80211_hdr *) enc,
enc + 24, enc_len - 24, &plain_len, NULL); enc + 24, enc_len - 24, &plain_len, NULL, NULL);
wpa_debug_level = MSG_EXCESSIVE; wpa_debug_level = MSG_EXCESSIVE;
os_free(enc); os_free(enc);

View file

@ -1,5 +1,5 @@
/* /*
* Temporal Key Integrity Protocol (CCMP) * Temporal Key Integrity Protocol (TKIP)
* Copyright (c) 2010, Jouni Malinen <j@w1.fi> * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
* *
* This software may be distributed under the terms of the BSD license. * This software may be distributed under the terms of the BSD license.
@ -291,7 +291,7 @@ static void michael_mic_hdr(const struct ieee80211_hdr *hdr11, u8 *hdr)
u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr, u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
const u8 *data, size_t data_len, size_t *decrypted_len, const u8 *data, size_t data_len, size_t *decrypted_len,
enum michael_mic_result *mic_res) enum michael_mic_result *mic_res, struct tkip_frag *frag)
{ {
u16 iv16; u16 iv16;
u32 iv32; u32 iv32;
@ -304,7 +304,11 @@ u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
u8 michael_hdr[16]; u8 michael_hdr[16];
u8 mic[8]; u8 mic[8];
u16 fc = le_to_host16(hdr->frame_control); u16 fc = le_to_host16(hdr->frame_control);
const u8 *full_payload;
size_t full_payload_len;
u16 sc = le_to_host16(hdr->seq_ctrl); u16 sc = le_to_host16(hdr->seq_ctrl);
u16 sn;
u8 fn;
if (data_len < 8 + 4) if (data_len < 8 + 4)
return NULL; return NULL;
@ -337,10 +341,49 @@ u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
} }
plain_len -= 4; plain_len -= 4;
/* TODO: MSDU reassembly */ full_payload = plain;
if ((fc & WLAN_FC_MOREFRAG) || WLAN_GET_SEQ_FRAG(sc) > 0) { full_payload_len = plain_len;
/* For now, return the decrypted fragment and do not check the
* Michael MIC value in the last fragment */ sn = WLAN_GET_SEQ_SEQ(sc);
fn = WLAN_GET_SEQ_FRAG(sc);
if (frag) {
/* MSDU reassembly for Michael MIC validation */
if (fn == 0 && (fc & WLAN_FC_MOREFRAG)) {
/* Start of a new fragmented MSDU */
wpabuf_free(frag->buf);
frag->buf = NULL;
frag->buf = wpabuf_alloc_copy(plain, plain_len);
os_memcpy(frag->ra, hdr->addr1, ETH_ALEN);
os_memcpy(frag->ta, hdr->addr2, ETH_ALEN);
frag->sn = sn;
frag->fn = 0;
}
if (frag->buf && (fn || (fc & WLAN_FC_MOREFRAG)) &&
sn == frag->sn && fn == frag->fn + 1 &&
os_memcmp(frag->ra, hdr->addr1, ETH_ALEN) == 0 &&
os_memcmp(frag->ta, hdr->addr2, ETH_ALEN) == 0) {
/* Add the next fragment */
if (wpabuf_resize(&frag->buf, plain_len) == 0) {
wpabuf_put_data(frag->buf, plain, plain_len);
frag->fn = fn;
if (!(fc & WLAN_FC_MOREFRAG)) {
full_payload = wpabuf_head(frag->buf);
full_payload_len =
wpabuf_len(frag->buf);
wpa_hexdump(MSG_MSGDUMP,
"TKIP reassembled full payload",
full_payload,
full_payload_len);
}
}
}
}
if ((fc & WLAN_FC_MOREFRAG) || (fn > 0 && full_payload == plain)) {
/* Return the decrypted fragment and do not check the
* Michael MIC value since no reassembled frame is available. */
*decrypted_len = plain_len; *decrypted_len = plain_len;
if (mic_res) { if (mic_res) {
*mic_res = MICHAEL_MIC_NOT_VERIFIED; *mic_res = MICHAEL_MIC_NOT_VERIFIED;
@ -348,7 +391,7 @@ u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
} }
} }
if (plain_len < 8) { if (full_payload_len < 8) {
wpa_printf(MSG_INFO, "TKIP: Not enough room for Michael MIC " wpa_printf(MSG_INFO, "TKIP: Not enough room for Michael MIC "
"in a frame from " MACSTR, MAC2STR(hdr->addr2)); "in a frame from " MACSTR, MAC2STR(hdr->addr2));
os_free(plain); os_free(plain);
@ -357,13 +400,14 @@ u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
michael_mic_hdr(hdr, michael_hdr); michael_mic_hdr(hdr, michael_hdr);
mic_key = tk + ((fc & WLAN_FC_FROMDS) ? 16 : 24); mic_key = tk + ((fc & WLAN_FC_FROMDS) ? 16 : 24);
michael_mic(mic_key, michael_hdr, plain, plain_len - 8, mic); michael_mic(mic_key, michael_hdr, full_payload, full_payload_len - 8,
if (os_memcmp(mic, plain + plain_len - 8, 8) != 0) { mic);
if (os_memcmp(mic, full_payload + full_payload_len - 8, 8) != 0) {
wpa_printf(MSG_INFO, "TKIP: Michael MIC mismatch in a frame " wpa_printf(MSG_INFO, "TKIP: Michael MIC mismatch in a frame "
"from " MACSTR, MAC2STR(hdr->addr2)); "from " MACSTR, MAC2STR(hdr->addr2));
wpa_hexdump(MSG_DEBUG, "TKIP: Calculated MIC", mic, 8); wpa_hexdump(MSG_DEBUG, "TKIP: Calculated MIC", mic, 8);
wpa_hexdump(MSG_DEBUG, "TKIP: Received MIC", wpa_hexdump(MSG_DEBUG, "TKIP: Received MIC",
plain + plain_len - 8, 8); full_payload + full_payload_len - 8, 8);
if (mic_res) { if (mic_res) {
*decrypted_len = plain_len - 8; *decrypted_len = plain_len - 8;
*mic_res = MICHAEL_MIC_INCORRECT; *mic_res = MICHAEL_MIC_INCORRECT;

View file

@ -110,6 +110,8 @@ static void wlantest_deinit(struct wlantest *wt)
clear_notes(wt); clear_notes(wt);
os_free(wt->decrypted); os_free(wt->decrypted);
wt->decrypted = NULL; wt->decrypted = NULL;
wpabuf_free(wt->tkip_frag.buf);
wt->tkip_frag.buf = NULL;
} }

View file

@ -184,6 +184,14 @@ struct wlantest_radius {
#define MAX_CTRL_CONNECTIONS 10 #define MAX_CTRL_CONNECTIONS 10
#define MAX_NOTES 10 #define MAX_NOTES 10
struct tkip_frag {
struct wpabuf *buf;
u8 ra[ETH_ALEN];
u8 ta[ETH_ALEN];
u16 sn;
u8 fn;
};
struct wlantest { struct wlantest {
int monitor_sock; int monitor_sock;
int monitor_wired; int monitor_wired;
@ -227,6 +235,8 @@ struct wlantest {
const char *write_file; const char *write_file;
const char *pcapng_file; const char *pcapng_file;
struct tkip_frag tkip_frag;
}; };
void add_note(struct wlantest *wt, int level, const char *fmt, ...) void add_note(struct wlantest *wt, int level, const char *fmt, ...)
@ -311,7 +321,7 @@ enum michael_mic_result {
}; };
u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr, u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
const u8 *data, size_t data_len, size_t *decrypted_len, const u8 *data, size_t data_len, size_t *decrypted_len,
enum michael_mic_result *mic_res); enum michael_mic_result *mic_res, struct tkip_frag *frag);
u8 * tkip_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos, u8 * tkip_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos,
u8 *pn, int keyid, size_t *encrypted_len); u8 *pn, int keyid, size_t *encrypted_len);
void tkip_get_pn(u8 *pn, const u8 *data); void tkip_get_pn(u8 *pn, const u8 *data);