wlantest: Add support for CCMP protection for injected frames
This commit is contained in:
parent
f6b25ca5b4
commit
571ab37b86
4 changed files with 267 additions and 2 deletions
124
wlantest/ccmp.c
124
wlantest/ccmp.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue