diff --git a/wlantest/rx_data.c b/wlantest/rx_data.c index 9e84b8a8e..16e0f53bc 100644 --- a/wlantest/rx_data.c +++ b/wlantest/rx_data.c @@ -177,7 +177,8 @@ static u8 * try_ptk(struct wlantest *wt, int pairwise_cipher, enum michael_mic_result mic_res; 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) add_note(wt, MSG_INFO, "Invalid Michael MIC"); else if (decrypted && mic_res == MICHAEL_MIC_NOT_VERIFIED) @@ -328,7 +329,7 @@ skip_replay_det: enum michael_mic_result mic_res; 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) add_note(wt, MSG_INFO, "Invalid Michael MIC"); else if (decrypted && mic_res == MICHAEL_MIC_NOT_VERIFIED) @@ -619,7 +620,7 @@ skip_replay_det: enum michael_mic_result mic_res; decrypted = tkip_decrypt(sta->ptk.tk, hdr, data, len, &dlen, - &mic_res); + &mic_res, &wt->tkip_frag); if (decrypted && mic_res == MICHAEL_MIC_INCORRECT) add_note(wt, MSG_INFO, "Invalid Michael MIC"); else if (decrypted && mic_res == MICHAEL_MIC_NOT_VERIFIED) diff --git a/wlantest/test_vectors.c b/wlantest/test_vectors.c index ca0449d31..7f39c4264 100644 --- a/wlantest/test_vectors.c +++ b/wlantest/test_vectors.c @@ -63,7 +63,7 @@ static void test_vector_tkip(void) wpa_debug_level = MSG_INFO; 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; os_free(enc); diff --git a/wlantest/tkip.c b/wlantest/tkip.c index 613a08aa7..843f6518a 100644 --- a/wlantest/tkip.c +++ b/wlantest/tkip.c @@ -1,5 +1,5 @@ /* - * Temporal Key Integrity Protocol (CCMP) + * Temporal Key Integrity Protocol (TKIP) * Copyright (c) 2010, Jouni Malinen * * 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, 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; u32 iv32; @@ -304,7 +304,11 @@ u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr, u8 michael_hdr[16]; u8 mic[8]; 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 sn; + u8 fn; if (data_len < 8 + 4) return NULL; @@ -337,10 +341,49 @@ u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr, } plain_len -= 4; - /* TODO: MSDU reassembly */ - if ((fc & WLAN_FC_MOREFRAG) || WLAN_GET_SEQ_FRAG(sc) > 0) { - /* For now, return the decrypted fragment and do not check the - * Michael MIC value in the last fragment */ + full_payload = plain; + full_payload_len = plain_len; + + 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; if (mic_res) { *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 " "in a frame from " MACSTR, MAC2STR(hdr->addr2)); os_free(plain); @@ -357,13 +400,14 @@ u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr, michael_mic_hdr(hdr, michael_hdr); mic_key = tk + ((fc & WLAN_FC_FROMDS) ? 16 : 24); - michael_mic(mic_key, michael_hdr, plain, plain_len - 8, mic); - if (os_memcmp(mic, plain + plain_len - 8, 8) != 0) { + michael_mic(mic_key, michael_hdr, full_payload, full_payload_len - 8, + mic); + if (os_memcmp(mic, full_payload + full_payload_len - 8, 8) != 0) { wpa_printf(MSG_INFO, "TKIP: Michael MIC mismatch in a frame " "from " MACSTR, MAC2STR(hdr->addr2)); wpa_hexdump(MSG_DEBUG, "TKIP: Calculated MIC", mic, 8); wpa_hexdump(MSG_DEBUG, "TKIP: Received MIC", - plain + plain_len - 8, 8); + full_payload + full_payload_len - 8, 8); if (mic_res) { *decrypted_len = plain_len - 8; *mic_res = MICHAEL_MIC_INCORRECT; diff --git a/wlantest/wlantest.c b/wlantest/wlantest.c index 62c89e226..ac20b068e 100644 --- a/wlantest/wlantest.c +++ b/wlantest/wlantest.c @@ -110,6 +110,8 @@ static void wlantest_deinit(struct wlantest *wt) clear_notes(wt); os_free(wt->decrypted); wt->decrypted = NULL; + wpabuf_free(wt->tkip_frag.buf); + wt->tkip_frag.buf = NULL; } diff --git a/wlantest/wlantest.h b/wlantest/wlantest.h index 4ea10c9e8..33ab42229 100644 --- a/wlantest/wlantest.h +++ b/wlantest/wlantest.h @@ -184,6 +184,14 @@ struct wlantest_radius { #define MAX_CTRL_CONNECTIONS 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 { int monitor_sock; int monitor_wired; @@ -227,6 +235,8 @@ struct wlantest { const char *write_file; const char *pcapng_file; + + struct tkip_frag tkip_frag; }; 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, 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 *pn, int keyid, size_t *encrypted_len); void tkip_get_pn(u8 *pn, const u8 *data);