]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
wlantest: Add support for decrypting WEP frames
authorJouni Malinen <jouni.malinen@atheros.com>
Fri, 14 Jan 2011 15:43:17 +0000 (17:43 +0200)
committerJouni Malinen <j@w1.fi>
Fri, 14 Jan 2011 15:43:17 +0000 (17:43 +0200)
wlantest/Makefile
wlantest/bss.c
wlantest/rx_data.c
wlantest/sta.c
wlantest/tkip.c
wlantest/wep.c [new file with mode: 0644]
wlantest/wlantest.c
wlantest/wlantest.h

index 378efab15ab21f4d17cc94f29c3d38b8ffdd7efb..c165ed41c80045fc74a7d7b47e5ebfd98259b4c7 100644 (file)
@@ -66,6 +66,7 @@ OBJS += ccmp.o
 OBJS += tkip.o
 OBJS += ctrl.o
 OBJS += inject.o
+OBJS += wep.o
 
 LIBS += -lpcap
 
index e7942af139a632cd135323a3fbce93c601b474f5..a4a315533cf93cb09b86a4557c1c2b6cfbbc9d14 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "utils/common.h"
 #include "common/defs.h"
+#include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "crypto/sha1.h"
 #include "wlantest.h"
@@ -132,6 +133,9 @@ void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
        struct wpa_ie_data data;
        int update = 0;
 
+       if (bss->capab_info != bss->prev_capab_info)
+               update = 1;
+
        if (elems->ssid == NULL || elems->ssid_len > 32) {
                wpa_printf(MSG_INFO, "Invalid or missing SSID in a Beacon "
                           "frame for " MACSTR, MAC2STR(bss->bssid));
@@ -195,6 +199,7 @@ void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
        if (!update)
                return;
 
+       bss->prev_capab_info = bss->capab_info;
        bss->proto = 0;
        bss->pairwise_cipher = 0;
        bss->group_cipher = 0;
@@ -236,6 +241,10 @@ void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
            !(bss->rsn_capab & WPA_CAPABILITY_MFPC))
                bss->mgmt_group_cipher = 0;
 
+       if (!bss->wpaie[0] && !bss->rsnie[0] &&
+           (bss->capab_info & WLAN_CAPABILITY_PRIVACY))
+               bss->group_cipher = WPA_CIPHER_WEP40;
+
        wpa_printf(MSG_INFO, "BSS " MACSTR
                   " proto=%s%s%s"
                   "pairwise=%s%s%s%s"
index a8312b6318048d592dfd3646a768153967ac25d7..95daaf224073e53d97190d0fb6aeec0b3fddb74f 100644 (file)
@@ -144,7 +144,8 @@ static void rx_data_bss_prot_group(struct wlantest *wt,
        }
 
        keyid = data[3] >> 6;
-       if (bss->gtk_len[keyid] == 0) {
+       if (bss->gtk_len[keyid] == 0 && bss->group_cipher != WPA_CIPHER_WEP40)
+       {
                wpa_printf(MSG_MSGDUMP, "No GTK known to decrypt the frame "
                           "(A2=" MACSTR " KeyID=%d)",
                           MAC2STR(hdr->addr2), keyid);
@@ -153,6 +154,8 @@ static void rx_data_bss_prot_group(struct wlantest *wt,
 
        if (bss->group_cipher == WPA_CIPHER_TKIP)
                tkip_get_pn(pn, data);
+       else if (bss->group_cipher == WPA_CIPHER_WEP40)
+               goto skip_replay_det;
        else
                ccmp_get_pn(pn, data);
        if (os_memcmp(pn, bss->rsc[keyid], 6) <= 0) {
@@ -167,9 +170,12 @@ static void rx_data_bss_prot_group(struct wlantest *wt,
                wpa_hexdump(MSG_INFO, "RSC", bss->rsc[keyid], 6);
        }
 
+skip_replay_det:
        if (bss->group_cipher == WPA_CIPHER_TKIP)
                decrypted = tkip_decrypt(bss->gtk[keyid], hdr, data, len,
                                         &dlen);
+       else if (bss->group_cipher == WPA_CIPHER_WEP40)
+               decrypted = wep_decrypt(wt, hdr, data, len, &dlen);
        else
                decrypted = ccmp_decrypt(bss->gtk[keyid], hdr, data, len,
                                         &dlen);
@@ -237,7 +243,9 @@ static void rx_data_bss_prot(struct wlantest *wt,
                        }
                }
        }
-       if ((sta == NULL || !sta->ptk_set) && tk == NULL) {
+       if ((sta == NULL ||
+            (!sta->ptk_set && sta->pairwise_cipher != WPA_CIPHER_WEP40)) &&
+           tk == NULL) {
                wpa_printf(MSG_MSGDUMP, "No PTK known to decrypt the frame");
                return;
        }
@@ -300,6 +308,8 @@ static void rx_data_bss_prot(struct wlantest *wt,
 
        if (tk == NULL && sta->pairwise_cipher == WPA_CIPHER_TKIP)
                tkip_get_pn(pn, data);
+       else if (sta->pairwise_cipher == WPA_CIPHER_WEP40)
+               goto skip_replay_det;
        else
                ccmp_get_pn(pn, data);
        if (os_memcmp(pn, rsc, 6) <= 0) {
@@ -314,10 +324,13 @@ static void rx_data_bss_prot(struct wlantest *wt,
                wpa_hexdump(MSG_INFO, "RSC", rsc, 6);
        }
 
+skip_replay_det:
        if (tk)
                decrypted = ccmp_decrypt(tk, hdr, data, len, &dlen);
        else if (sta->pairwise_cipher == WPA_CIPHER_TKIP)
                decrypted = tkip_decrypt(sta->ptk.tk1, hdr, data, len, &dlen);
+       else if (sta->pairwise_cipher == WPA_CIPHER_WEP40)
+               decrypted = wep_decrypt(wt, hdr, data, len, &dlen);
        else
                decrypted = ccmp_decrypt(sta->ptk.tk1, hdr, data, len, &dlen);
        if (decrypted) {
index bfa6ee0b0e883a37f3d0fa68363aad9c74d3c0dc..6e012b8cb7f7ad5fec595257ea507ca59aee0456 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "utils/common.h"
 #include "common/defs.h"
+#include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "wlantest.h"
 
@@ -97,8 +98,16 @@ void sta_update_assoc(struct wlantest_sta *sta, struct ieee802_11_elems *elems)
                        wpa_printf(MSG_INFO, "Failed to parse WPA IE from "
                                   MACSTR, MAC2STR(sta->addr));
                }
-       } else
+       } else {
                sta->rsnie[0] = 0;
+               sta->proto = 0;
+               sta->pairwise_cipher = 0;
+               sta->key_mgmt = 0;
+               sta->rsn_capab = 0;
+               if (sta->assocreq_capab_info & WLAN_CAPABILITY_PRIVACY)
+                       sta->pairwise_cipher = WPA_CIPHER_WEP40;
+               goto skip_rsn_wpa;
+       }
 
        sta->proto = data.proto;
        sta->pairwise_cipher = data.pairwise_cipher;
@@ -130,6 +139,7 @@ void sta_update_assoc(struct wlantest_sta *sta, struct ieee802_11_elems *elems)
                           "MFPR", MAC2STR(sta->addr), MAC2STR(bss->bssid));
        }
 
+skip_rsn_wpa:
        wpa_printf(MSG_INFO, "STA " MACSTR
                   " proto=%s%s%s"
                   "pairwise=%s%s%s%s"
index 3da64c5ac81378201be1a9932bcbc98f29cc7625..3533914bc7b8a2907f464c5bbfb3575846127782 100644 (file)
@@ -19,6 +19,9 @@
 #include "wlantest.h"
 
 
+void wep_crypt(u8 *key, u8 *buf, size_t plen);
+
+
 static inline u16 RotR1(u16 val)
 {
        return (val >> 1) | (val << 15);
@@ -172,35 +175,6 @@ static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK,
 }
 
 
-static void wep_crypt(u8 *key, u8 *buf, size_t plen)
-{
-       u32 i, j, k;
-       u8 S[256];
-#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
-       u8 *pos;
-
-       /* Setup RC4 state */
-       for (i = 0; i < 256; i++)
-               S[i] = i;
-       j = 0;
-       for (i = 0; i < 256; i++) {
-               j = (j + S[i] + key[i & 0x0f]) & 0xff;
-               S_SWAP(i, j);
-       }
-
-       /* Apply RC4 to data */
-       pos = buf;
-       i = j = 0;
-       for (k = 0; k < plen; k++) {
-               i = (i + 1) & 0xff;
-               j = (j + S[i]) & 0xff;
-               S_SWAP(i, j);
-               *pos ^= S[(S[i] + S[j]) & 0xff];
-               pos++;
-       }
-}
-
-
 static inline u32 rotl(u32 val, int bits)
 {
        return (val << bits) | (val >> (32 - bits));
diff --git a/wlantest/wep.c b/wlantest/wep.c
new file mode 100644 (file)
index 0000000..13ff218
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Wired Equivalent Privacy (WEP)
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "wlantest.h"
+
+
+void wep_crypt(u8 *key, u8 *buf, size_t plen)
+{
+       u32 i, j, k;
+       u8 S[256];
+#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+       u8 *pos;
+
+       /* Setup RC4 state */
+       for (i = 0; i < 256; i++)
+               S[i] = i;
+       j = 0;
+       for (i = 0; i < 256; i++) {
+               j = (j + S[i] + key[i & 0x0f]) & 0xff;
+               S_SWAP(i, j);
+       }
+
+       /* Apply RC4 to data */
+       pos = buf;
+       i = j = 0;
+       for (k = 0; k < plen; k++) {
+               i = (i + 1) & 0xff;
+               j = (j + S[i]) & 0xff;
+               S_SWAP(i, j);
+               *pos ^= S[(S[i] + S[j]) & 0xff];
+               pos++;
+       }
+}
+
+
+static int try_wep(const u8 *key, size_t key_len, const u8 *data,
+                  size_t data_len, u8 *plain)
+{
+       u32 icv, rx_icv;
+       u8 k[16];
+       int i, j;
+
+       for (i = 0, j = 0; i < sizeof(k); i++) {
+               k[i] = key[j];
+               j++;
+               if (j >= key_len)
+                       j = 0;
+       }
+
+       os_memcpy(plain, data, data_len);
+       wep_crypt(k, plain, data_len);
+       icv = crc32(plain, data_len - 4);
+       rx_icv = WPA_GET_LE32(plain + data_len - 4);
+       if (icv != rx_icv)
+               return -1;
+
+       return 0;
+}
+
+
+u8 * wep_decrypt(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+                const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+       u8 *plain;
+       struct wlantest_wep *w;
+       int found = 0;
+       u8 key[16];
+
+       if (dl_list_empty(&wt->wep))
+               return NULL;
+
+       if (data_len < 4 + 4)
+               return NULL;
+       plain = os_malloc(data_len - 4);
+       if (plain == NULL)
+               return NULL;
+
+       dl_list_for_each(w, &wt->wep, struct wlantest_wep, list) {
+               os_memcpy(key, data, 3);
+               os_memcpy(key + 3, w->key, w->key_len);
+               if (try_wep(key, 3 + w->key_len, data + 4, data_len - 4, plain)
+                   == 0) {
+                       found = 1;
+                       break;
+               }
+       }
+       if (!found) {
+               os_free(plain);
+               return NULL;
+       }
+
+       *decrypted_len = data_len - 4 - 4;
+       return plain;
+}
index a98513a0ccec58bf5bc5715e7e9d44114da95c14..17b3dd3379f3a9dfc635c70a3a561eeabf1906b7 100644 (file)
@@ -66,6 +66,7 @@ static void wlantest_init(struct wlantest *wt)
        dl_list_init(&wt->secret);
        dl_list_init(&wt->radius);
        dl_list_init(&wt->pmk);
+       dl_list_init(&wt->wep);
 }
 
 
@@ -82,6 +83,7 @@ static void wlantest_deinit(struct wlantest *wt)
        struct wlantest_radius_secret *s, *sn;
        struct wlantest_radius *r, *rn;
        struct wlantest_pmk *pmk, *np;
+       struct wlantest_wep *wep, *nw;
 
        if (wt->ctrl_sock >= 0)
                ctrl_deinit(wt);
@@ -98,6 +100,8 @@ static void wlantest_deinit(struct wlantest *wt)
                radius_deinit(r);
        dl_list_for_each_safe(pmk, np, &wt->pmk, struct wlantest_pmk, list)
                pmk_deinit(pmk);
+       dl_list_for_each_safe(wep, nw, &wt->wep, struct wlantest_wep, list)
+               os_free(wep);
        write_pcap_deinit(wt);
 }
 
@@ -132,6 +136,28 @@ static void add_secret(struct wlantest *wt, const char *secret)
 }
 
 
+static void add_wep(struct wlantest *wt, const char *key)
+{
+       struct wlantest_wep *w;
+       size_t len = os_strlen(key);
+
+       if (len != 2 * 5 && len != 2 * 13) {
+               wpa_printf(MSG_INFO, "Invalid WEP key '%s'", key);
+               return;
+       }
+       w = os_zalloc(sizeof(*w));
+       if (w == NULL)
+               return;
+       if (hexstr2bin(key, w->key, len / 2) < 0) {
+               os_free(w);
+               wpa_printf(MSG_INFO, "Invalid WEP key '%s'", key);
+               return;
+       }
+       w->key_len = len / 2;
+       dl_list_add(&wt->wep, &w->list);
+}
+
+
 int main(int argc, char *argv[])
 {
        int c;
@@ -152,7 +178,7 @@ int main(int argc, char *argv[])
        wlantest_init(&wt);
 
        for (;;) {
-               c = getopt(argc, argv, "cdhi:I:p:P:qr:R:w:");
+               c = getopt(argc, argv, "cdhi:I:p:P:qr:R:w:W:");
                if (c < 0)
                        break;
                switch (c) {
@@ -190,6 +216,9 @@ int main(int argc, char *argv[])
                case 'w':
                        write_file = optarg;
                        break;
+               case 'W':
+                       add_wep(&wt, optarg);
+                       break;
                default:
                        usage();
                        return -1;
index d93834642d08fe12d3919fe99287e599fd915a35..4301504e8e763d52c101fe40470adec5edc8ab26 100644 (file)
@@ -44,6 +44,12 @@ struct wlantest_pmk {
        u8 pmk[32];
 };
 
+struct wlantest_wep {
+       struct dl_list list;
+       size_t key_len;
+       u8 key[13];
+};
+
 struct wlantest_sta {
        struct dl_list list;
        struct wlantest_bss *bss;
@@ -106,6 +112,7 @@ struct wlantest_bss {
        struct dl_list list;
        u8 bssid[ETH_ALEN];
        u16 capab_info;
+       u16 prev_capab_info;
        u8 ssid[32];
        size_t ssid_len;
        int proberesp_seen;
@@ -154,6 +161,7 @@ struct wlantest {
        struct dl_list secret; /* struct wlantest_radius_secret */
        struct dl_list radius; /* struct wlantest_radius */
        struct dl_list pmk; /* struct wlantest_pmk */
+       struct dl_list wep; /* struct wlantest_wep */
 
        unsigned int rx_mgmt;
        unsigned int rx_ctrl;
@@ -223,6 +231,9 @@ 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);
 
+u8 * wep_decrypt(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+                const u8 *data, size_t data_len, size_t *decrypted_len);
+
 int ctrl_init(struct wlantest *wt);
 void ctrl_deinit(struct wlantest *wt);