]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
wlantest: Add support for CCMP protection for injected frames
authorJouni Malinen <jouni.malinen@atheros.com>
Fri, 19 Nov 2010 14:09:19 +0000 (16:09 +0200)
committerJouni Malinen <j@w1.fi>
Fri, 19 Nov 2010 14:09:19 +0000 (16:09 +0200)
wlantest/ccmp.c
wlantest/inject.c
wlantest/tkip.c
wlantest/wlantest.h

index 7bb2d2e2af17b5a9cf5f7f9ac6b90267d421684a..6b2b45e53c093089ef106a4280c59f2b31f72869 100644 (file)
@@ -222,3 +222,127 @@ void ccmp_get_pn(u8 *pn, const u8 *data)
        pn[4] = data[1]; /* PN1 */
        pn[5] = data[0]; /* PN0 */
 }
+
+
+u8 * ccmp_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos,
+                 u8 *pn, int keyid, size_t *encrypted_len)
+{
+       u8 aad[2 + 30], nonce[13];
+       size_t aad_len;
+       u8 b[AES_BLOCK_SIZE], x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE];
+       void *aes;
+       u8 *crypt, *pos, *ppos, *mpos;
+       size_t plen, last;
+       struct ieee80211_hdr *hdr;
+       int i;
+
+       if (len < hdrlen || hdrlen < 24)
+               return NULL;
+       plen = len - hdrlen;
+       last = plen % AES_BLOCK_SIZE;
+
+       crypt = os_malloc(hdrlen + 8 + plen + 8);
+       if (crypt == NULL)
+               return NULL;
+
+       os_memcpy(crypt, frame, hdrlen);
+       hdr = (struct ieee80211_hdr *) crypt;
+       hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+       pos = crypt + hdrlen;
+       *pos++ = pn[5]; /* PN0 */
+       *pos++ = pn[4]; /* PN1 */
+       *pos++ = 0x20 | (keyid << 6);
+       *pos++ = pn[3]; /* PN2 */
+       *pos++ = pn[2]; /* PN3 */
+       *pos++ = pn[1]; /* PN4 */
+       *pos++ = pn[0]; /* PN5 */
+
+       aes = aes_encrypt_init(tk, 16);
+       if (aes == NULL) {
+               os_free(crypt);
+               return NULL;
+       }
+
+       os_memset(aad, 0, sizeof(aad));
+       ccmp_aad_nonce(hdr, crypt + hdrlen, &aad[2], &aad_len, nonce);
+       WPA_PUT_BE16(aad, aad_len);
+       wpa_hexdump(MSG_EXCESSIVE, "CCMP AAD", &aad[2], aad_len);
+       wpa_hexdump(MSG_EXCESSIVE, "CCMP nonce", nonce, 13);
+
+       /* Authentication */
+       /* B_0: Flags | Nonce N | l(m) */
+       b[0] = 0x40 /* Adata */ | (3 /* M' */ << 3) | 1 /* L' */;
+       os_memcpy(&b[1], nonce, 13);
+       WPA_PUT_BE16(&b[14], plen);
+
+       wpa_hexdump(MSG_EXCESSIVE, "CCMP B_0", b, AES_BLOCK_SIZE);
+       aes_encrypt(aes, b, x); /* X_1 = E(K, B_0) */
+
+       wpa_hexdump(MSG_EXCESSIVE, "CCMP B_1", aad, AES_BLOCK_SIZE);
+       xor_aes_block(aad, x);
+       aes_encrypt(aes, aad, x); /* X_2 = E(K, X_1 XOR B_1) */
+
+       wpa_hexdump(MSG_EXCESSIVE, "CCMP B_2", &aad[AES_BLOCK_SIZE],
+                   AES_BLOCK_SIZE);
+       xor_aes_block(&aad[AES_BLOCK_SIZE], x);
+       aes_encrypt(aes, &aad[AES_BLOCK_SIZE], x); /* X_3 = E(K, X_2 XOR B_2)
+                                                   */
+
+       ppos = frame + hdrlen;
+       for (i = 0; i < plen / AES_BLOCK_SIZE; i++) {
+               /* X_i+1 = E(K, X_i XOR B_i) */
+               xor_aes_block(x, ppos);
+               ppos += AES_BLOCK_SIZE;
+               aes_encrypt(aes, x, x);
+       }
+       if (last) {
+               /* XOR zero-padded last block */
+               for (i = 0; i < last; i++)
+                       x[i] ^= *ppos++;
+               aes_encrypt(aes, x, x);
+       }
+
+       /* Encryption */
+
+       /* CCM: M=8 L=2, Adata=1, M' = (M-2)/2 = 3, L' = L-1 = 1 */
+
+       /* A_i = Flags | Nonce N | Counter i */
+       a[0] = 0x01; /* Flags = L' */
+       os_memcpy(&a[1], nonce, 13);
+
+       ppos = crypt + hdrlen + 8;
+
+       /* crypt = msg XOR (S_1 | S_2 | ... | S_n) */
+       mpos = frame + hdrlen;
+       for (i = 1; i <= plen / AES_BLOCK_SIZE; i++) {
+               WPA_PUT_BE16(&a[14], i);
+               /* S_i = E(K, A_i) */
+               aes_encrypt(aes, a, ppos);
+               xor_aes_block(ppos, mpos);
+               ppos += AES_BLOCK_SIZE;
+               mpos += AES_BLOCK_SIZE;
+       }
+       if (last) {
+               WPA_PUT_BE16(&a[14], i);
+               aes_encrypt(aes, a, ppos);
+               /* XOR zero-padded last block */
+               for (i = 0; i < last; i++)
+                       *ppos++ ^= *mpos++;
+       }
+
+       wpa_hexdump(MSG_EXCESSIVE, "CCMP T", x, 8);
+       /* U = T XOR S_0; S_0 = E(K, A_0) */
+       WPA_PUT_BE16(&a[14], 0);
+       aes_encrypt(aes, a, b);
+       for (i = 0; i < 8; i++)
+               ppos[i] = x[i] ^ b[i];
+       wpa_hexdump(MSG_EXCESSIVE, "CCMP U", ppos, 8);
+
+       wpa_hexdump(MSG_EXCESSIVE, "CCMP encrypted", crypt + hdrlen + 8, plen);
+
+       aes_encrypt_deinit(aes);
+
+       *encrypted_len = hdrlen + 8 + plen + 8;
+
+       return crypt;
+}
index f8ee02639269d54ae335c4d8e8adace1c7ce2201..cdfa37a537255fbc2d61fe87e3834c70b88cc06c 100644 (file)
@@ -15,6 +15,8 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "common/defs.h"
+#include "common/ieee802_11_defs.h"
 #include "wlantest.h"
 
 
@@ -58,11 +60,99 @@ static int inject_frame(int s, const void *data, size_t len)
 }
 
 
+static int is_robust_mgmt(u8 *frame, size_t len)
+{
+       struct ieee80211_mgmt *mgmt;
+       u16 fc, stype;
+       if (len < 24)
+               return 0;
+       mgmt = (struct ieee80211_mgmt *) frame;
+       fc = le_to_host16(mgmt->frame_control);
+       if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
+               return 0;
+       stype = WLAN_FC_GET_STYPE(fc);
+       if (stype == WLAN_FC_STYPE_DEAUTH || stype == WLAN_FC_STYPE_DISASSOC)
+               return 1;
+       if (stype == WLAN_FC_STYPE_ACTION) {
+               if (len < 25)
+                       return 0;
+               if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
+                       return 1;
+       }
+       return 0;
+}
+
+
+static int wlantest_inject_prot(struct wlantest *wt, struct wlantest_bss *bss,
+                               struct wlantest_sta *sta, u8 *frame,
+                               size_t len, int incorrect_key)
+{
+       u8 *crypt;
+       size_t crypt_len;
+       int ret;
+       u8 dummy[64];
+       u8 *pn;
+       struct ieee80211_hdr *hdr;
+       u16 fc;
+       int tid = 0;
+       u8 *qos = NULL;
+       int hdrlen;
+
+       if (sta == NULL)
+               return -1; /* TODO: add support for group Data and BIP */
+
+       if (!sta->ptk_set)
+               return -1;
+
+       hdr = (struct ieee80211_hdr *) frame;
+       hdrlen = 24;
+       fc = le_to_host16(hdr->frame_control);
+       if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
+               tid = 16;
+       else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) {
+               if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+                   (WLAN_FC_TODS | WLAN_FC_FROMDS))
+                       hdrlen += ETH_ALEN;
+               if (WLAN_FC_GET_STYPE(fc) & 0x08) {
+                       qos = frame + hdrlen;
+                       hdrlen += 2;
+                       tid = qos[0] & 0x0f;
+               }
+       }
+       if (os_memcmp(hdr->addr2, bss->bssid, ETH_ALEN) == 0)
+               pn = sta->rsc_fromds[tid];
+       else
+               pn = sta->rsc_tods[tid];
+       inc_byte_array(pn, 6);
+
+       os_memset(dummy, 0x11, sizeof(dummy));
+       if (sta->pairwise_cipher == WPA_CIPHER_TKIP)
+               crypt = tkip_encrypt(incorrect_key ? dummy : sta->ptk.tk1,
+                                    frame, len, hdrlen, qos, pn, 0,
+                                    &crypt_len);
+       else
+               crypt = ccmp_encrypt(incorrect_key ? dummy : sta->ptk.tk1,
+                                    frame, len, hdrlen, qos, pn, 0,
+                                    &crypt_len);
+
+       if (crypt == NULL)
+               return -1;
+
+       ret = inject_frame(wt->monitor_sock, crypt, crypt_len);
+       os_free(crypt);
+
+       return (ret < 0) ? -1 : 0;
+}
+
+
 int wlantest_inject(struct wlantest *wt, struct wlantest_bss *bss,
                    struct wlantest_sta *sta, u8 *frame, size_t len,
                    enum wlantest_inject_protection prot)
 {
        int ret;
+       struct ieee80211_hdr *hdr;
+       u16 fc;
+       int protectable, protect = 0;
 
        wpa_hexdump(MSG_DEBUG, "Inject frame", frame, len);
        if (wt->monitor_sock < 0) {
@@ -71,9 +161,48 @@ int wlantest_inject(struct wlantest *wt, struct wlantest_bss *bss,
                return -1;
        }
 
-       /* TODO: encrypt if needed */
-       if (prot != WLANTEST_INJECT_UNPROTECTED)
+       hdr = (struct ieee80211_hdr *) frame;
+       fc = le_to_host16(hdr->frame_control);
+       protectable = WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA ||
+               is_robust_mgmt(frame, len);
+
+       if (prot == WLANTEST_INJECT_PROTECTED ||
+           prot == WLANTEST_INJECT_INCORRECT_KEY) {
+               if (!sta) {
+                       wpa_printf(MSG_INFO, "Broadcast protection not yet "
+                                  "implemented");
+                       return -1;
+               }
+               if (sta && !sta->ptk_set) {
+                       wpa_printf(MSG_INFO, "No PTK known for the STA " MACSTR
+                                  " to encrypt the injected frame",
+                                  MAC2STR(sta->addr));
+                       return -1;
+               }
+               protect = 1;
+       } else if (protectable && prot != WLANTEST_INJECT_UNPROTECTED) {
+               if (sta && sta->ptk_set)
+                       protect = 1;
+               else if (!sta) {
+                       if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
+                           (bss->gtk_len[1] || bss->gtk_len[2]))
+                               protect = 1;
+                       if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+                           (bss->igtk_set[4] || bss->igtk_set[5]))
+                               protect = 1;
+               }
+       }
+
+       if ((prot == WLANTEST_INJECT_PROTECTED ||
+            prot == WLANTEST_INJECT_INCORRECT_KEY) && !protect) {
+               wpa_printf(MSG_INFO, "Cannot protect injected frame");
                return -1;
+       }
+
+       if (protect)
+               return wlantest_inject_prot(
+                       wt, bss, sta, frame, len,
+                       prot == WLANTEST_INJECT_INCORRECT_KEY);
 
        ret = inject_frame(wt->monitor_sock, frame, len);
        return (ret < 0) ? -1 : 0;
index 2f8d28d94637c7d880999f3d6e51a9a6249d0eca..3da64c5ac81378201be1a9932bcbc98f29cc7625 100644 (file)
@@ -403,3 +403,11 @@ void tkip_get_pn(u8 *pn, const u8 *data)
        pn[4] = data[0]; /* PN1 */
        pn[5] = data[2]; /* PN0 */
 }
+
+
+u8 * tkip_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos,
+                 u8 *pn, int keyid, size_t *encrypted_len)
+{
+       /* TODO */
+       return NULL;
+}
index 44574d025efa012438d71305bc24257530f5a797..14c31b777d294e766771cdf3ca7236f96bf638f1 100644 (file)
@@ -161,10 +161,14 @@ void sta_update_assoc(struct wlantest_sta *sta,
 
 u8 * ccmp_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
                  const u8 *data, size_t data_len, size_t *decrypted_len);
+u8 * ccmp_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos,
+                 u8 *pn, int keyid, size_t *encrypted_len);
 void ccmp_get_pn(u8 *pn, const u8 *data);
 
 u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
                  const u8 *data, size_t data_len, size_t *decrypted_len);
+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);
 
 int ctrl_init(struct wlantest *wt);