/*
* Copyright 2002-2004, Instant802 Networks, Inc.
* Copyright 2008, Jouni Malinen <j@w1.fi>
* Copyright (C) 2016 Intel Deutschland GmbH
*
* 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.
*/
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/compiler.h>
#include <linux/ieee80211.h>
#include <linux/gfp.h>
#include <asm/unaligned.h>
#include <net/mac80211.h>
#include <crypto/aes.h>
#include <crypto/algapi.h>
#include "ieee80211_i.h"
#include "michael.h"
#include "tkip.h"
#include "aes_ccm.h"
#include "aes_cmac.h"
#include "aes_gmac.h"
#include "aes_gcm.h"
#include "wpa.h"
ieee80211_tx_result
ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
{
u8 *data, *key, *mic;
size_t data_len;
unsigned int hdrlen;
struct ieee80211_hdr *hdr;
struct sk_buff *skb = tx->skb;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int tail;
hdr = (struct ieee80211_hdr *)skb->data;
if (!tx->key || tx->key->conf.cipher != WLAN_CIPHER_SUITE_TKIP ||
skb->len < 24 || !ieee80211_is_data_present(hdr->frame_control))
return TX_CONTINUE;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (skb->len < hdrlen)
return TX_DROP;
data = skb->data + hdrlen;
data_len = skb->len - hdrlen;
if (unlikely(info->flags & IEEE80211_TX_INTFL_TKIP_MIC_FAILURE)) {
/* Need to use software crypto for the test */
info->control.hw_key = NULL;
}
if (info->control.hw_key &&
(info->flags & IEEE80211_TX_CTL_DONTFRAG ||
tx->local->ops->set_frag_threshold) &&
!(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) {
/* hwaccel - with no need for SW-generated MMIC */
return TX_CONTINUE;
}
tail = MICHAEL_MIC_LEN;
if (!info->control.hw_key)
tail += IEEE80211_TKIP_ICV_LEN;
if (WARN(skb_tailroom(skb) < tail ||
skb_headroom(skb) < IEEE80211_TKIP_IV_LEN,
"mmic: not enough head/tail (%d/%d,%d/%d)\n",
skb_headroom(skb), IEEE80211_TKIP_IV_LEN,
skb_tailroom(skb), tail))
return TX_DROP;
key = &tx->key->conf.key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY];
mic = skb_put(skb, MICHAEL_MIC_LEN);
michael_mic(key, hdr, data, data_len, mic);
if (unlikely(info->flags & IEEE80211_TX_INTFL_TKIP_MIC_FAILURE))
mic[0]++;
return TX_CONTINUE;
}
ieee80211_rx_result
ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
{
u8 *data, *key = NULL;
size_t data_len;
unsigned int hdrlen;
u8 mic[MICHAEL_MIC_LEN];
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
/*
* it makes no sense to check for MIC errors on anything other
* than data frames.
*/
if (!ieee80211_is_data_present(hdr->frame_control))
return RX_CONTINUE;
/*
* No way to verify the MIC if the hardware stripped it or
* the IV with the key index. In this case we have solely rely
* on the driver to set RX_FLAG_MMIC_ERROR in the event of a
* MIC failure report.
*/
if (status->flag & (<