2007-05-05 22:45:53 +04:00
/*
* Copyright 2002 - 2004 , Instant802 Networks , Inc .
2009-01-08 14:32:01 +03:00
* Copyright 2008 , Jouni Malinen < j @ w1 . fi >
2007-05-05 22:45:53 +04:00
*
* 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/slab.h>
# include <linux/skbuff.h>
# include <linux/compiler.h>
2008-07-02 22:05:35 +04:00
# include <linux/ieee80211.h>
# include <asm/unaligned.h>
2007-05-05 22:45:53 +04:00
# include <net/mac80211.h>
2007-08-29 01:01:53 +04:00
2007-05-05 22:45:53 +04:00
# include "ieee80211_i.h"
# include "michael.h"
# include "tkip.h"
# include "aes_ccm.h"
2009-01-08 14:32:01 +03:00
# include "aes_cmac.h"
2007-05-05 22:45:53 +04:00
# include "wpa.h"
2008-01-31 21:48:20 +03:00
ieee80211_tx_result
2008-02-25 18:27:43 +03:00
ieee80211_tx_h_michael_mic_add ( struct ieee80211_tx_data * tx )
2007-05-05 22:45:53 +04:00
{
2008-07-02 22:05:35 +04:00
u8 * data , * key , * mic , key_offset ;
2007-05-05 22:45:53 +04:00
size_t data_len ;
2008-07-02 22:05:35 +04:00
unsigned int hdrlen ;
struct ieee80211_hdr * hdr ;
2007-05-05 22:45:53 +04:00
struct sk_buff * skb = tx - > skb ;
2010-01-17 03:47:58 +03:00
struct ieee80211_tx_info * info = IEEE80211_SKB_CB ( skb ) ;
2007-05-05 22:45:53 +04:00
int authenticator ;
2008-05-29 12:38:53 +04:00
int tail ;
2007-05-05 22:45:53 +04:00
2008-07-03 03:30:52 +04:00
hdr = ( struct ieee80211_hdr * ) skb - > data ;
2007-08-29 01:01:54 +04:00
if ( ! tx - > key | | tx - > key - > conf . alg ! = ALG_TKIP | | skb - > len < 24 | |
2008-07-03 03:30:52 +04:00
! ieee80211_is_data_present ( hdr - > frame_control ) )
2008-01-31 21:48:20 +03:00
return TX_CONTINUE ;
2007-05-05 22:45:53 +04:00
2008-07-02 22:05:35 +04:00
hdrlen = ieee80211_hdrlen ( hdr - > frame_control ) ;
if ( skb - > len < hdrlen )
2008-01-31 21:48:20 +03:00
return TX_DROP ;
2007-05-05 22:45:53 +04:00
2008-07-02 22:05:35 +04:00
data = skb - > data + hdrlen ;
data_len = skb - > len - hdrlen ;
2010-01-17 03:47:58 +03:00
if ( info - > control . hw_key & &
2008-02-25 18:27:43 +03:00
! ( tx - > flags & IEEE80211_TX_FRAGMENTED ) & &
2010-01-17 03:47:58 +03:00
! ( tx - > key - > conf . flags & IEEE80211_KEY_FLAG_GENERATE_MMIC ) ) {
/* hwaccel - with no need for SW-generated MMIC */
2008-01-31 21:48:20 +03:00
return TX_CONTINUE ;
2007-05-05 22:45:53 +04:00
}
2008-05-29 12:38:53 +04:00
tail = MICHAEL_MIC_LEN ;
2010-01-17 03:47:58 +03:00
if ( ! info - > control . hw_key )
2008-05-29 12:38:53 +04:00
tail + = TKIP_ICV_LEN ;
if ( WARN_ON ( skb_tailroom ( skb ) < tail | |
skb_headroom ( skb ) < TKIP_IV_LEN ) )
return TX_DROP ;
2007-05-05 22:45:53 +04:00
#if 0
authenticator = fc & IEEE80211_FCTL_FROMDS ; /* FIX */
# else
authenticator = 1 ;
# endif
2008-06-21 18:02:46 +04:00
key_offset = authenticator ?
NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY :
NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY ;
key = & tx - > key - > conf . key [ key_offset ] ;
2007-05-05 22:45:53 +04:00
mic = skb_put ( skb , MICHAEL_MIC_LEN ) ;
2008-07-02 22:05:35 +04:00
michael_mic ( key , hdr , data , data_len , mic ) ;
2007-05-05 22:45:53 +04:00
2008-01-31 21:48:20 +03:00
return TX_CONTINUE ;
2007-05-05 22:45:53 +04:00
}
2008-01-31 21:48:20 +03:00
ieee80211_rx_result
2008-02-25 18:27:43 +03:00
ieee80211_rx_h_michael_mic_verify ( struct ieee80211_rx_data * rx )
2007-05-05 22:45:53 +04:00
{
2008-07-02 22:05:35 +04:00
u8 * data , * key = NULL , key_offset ;
2007-05-05 22:45:53 +04:00
size_t data_len ;
2008-07-02 22:05:35 +04:00
unsigned int hdrlen ;
2007-05-05 22:45:53 +04:00
u8 mic [ MICHAEL_MIC_LEN ] ;
struct sk_buff * skb = rx - > skb ;
2009-11-16 15:58:20 +03:00
struct ieee80211_rx_status * status = IEEE80211_SKB_RXCB ( skb ) ;
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2007-05-05 22:45:53 +04:00
int authenticator = 1 , wpa_test = 0 ;
2008-10-07 14:04:32 +04:00
/* No way to verify the MIC if the hardware stripped it */
2009-11-16 15:58:20 +03:00
if ( status - > flag & RX_FLAG_MMIC_STRIPPED )
2008-01-31 21:48:20 +03:00
return RX_CONTINUE ;
2007-05-05 22:45:53 +04:00
2007-08-29 01:01:54 +04:00
if ( ! rx - > key | | rx - > key - > conf . alg ! = ALG_TKIP | |
2008-07-03 03:30:52 +04:00
! ieee80211_has_protected ( hdr - > frame_control ) | |
! ieee80211_is_data_present ( hdr - > frame_control ) )
2008-01-31 21:48:20 +03:00
return RX_CONTINUE ;
2007-05-05 22:45:53 +04:00
2008-07-02 22:05:35 +04:00
hdrlen = ieee80211_hdrlen ( hdr - > frame_control ) ;
if ( skb - > len < hdrlen + MICHAEL_MIC_LEN )
2008-01-31 21:48:21 +03:00
return RX_DROP_UNUSABLE ;
2007-05-05 22:45:53 +04:00
2008-07-02 22:05:35 +04:00
data = skb - > data + hdrlen ;
data_len = skb - > len - hdrlen - MICHAEL_MIC_LEN ;
2007-05-05 22:45:53 +04:00
#if 0
authenticator = fc & IEEE80211_FCTL_TODS ; /* FIX */
# else
authenticator = 1 ;
# endif
2008-06-21 18:02:46 +04:00
key_offset = authenticator ?
NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY :
NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY ;
key = & rx - > key - > conf . key [ key_offset ] ;
2008-07-02 22:05:35 +04:00
michael_mic ( key , hdr , data , data_len , mic ) ;
2007-05-05 22:45:53 +04:00
if ( memcmp ( mic , data + data_len , MICHAEL_MIC_LEN ) ! = 0 | | wpa_test ) {
2008-02-25 18:27:43 +03:00
if ( ! ( rx - > flags & IEEE80211_RX_RA_MATCH ) )
2008-01-31 21:48:21 +03:00
return RX_DROP_UNUSABLE ;
2007-05-05 22:45:53 +04:00
2008-08-03 04:04:37 +04:00
mac80211_ev_michael_mic_failure ( rx - > sdata , rx - > key - > conf . keyidx ,
2009-07-01 23:26:47 +04:00
( void * ) skb - > data , NULL ,
GFP_ATOMIC ) ;
2008-01-31 21:48:21 +03:00
return RX_DROP_UNUSABLE ;
2007-05-05 22:45:53 +04:00
}
/* remove Michael MIC from payload */
skb_trim ( skb , skb - > len - MICHAEL_MIC_LEN ) ;
2007-09-26 17:19:45 +04:00
/* update IV in key information to be able to detect replays */
2008-05-15 03:26:19 +04:00
rx - > key - > u . tkip . rx [ rx - > queue ] . iv32 = rx - > tkip_iv32 ;
rx - > key - > u . tkip . rx [ rx - > queue ] . iv16 = rx - > tkip_iv16 ;
2007-09-26 17:19:45 +04:00
2008-01-31 21:48:20 +03:00
return RX_CONTINUE ;
2007-05-05 22:45:53 +04:00
}
2008-05-15 14:55:29 +04:00
static int tkip_encrypt_skb ( struct ieee80211_tx_data * tx , struct sk_buff * skb )
2007-05-05 22:45:53 +04:00
{
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
struct ieee80211_key * key = tx - > key ;
2008-05-15 14:55:29 +04:00
struct ieee80211_tx_info * info = IEEE80211_SKB_CB ( skb ) ;
2008-06-12 01:21:58 +04:00
unsigned int hdrlen ;
int len , tail ;
2007-05-05 22:45:53 +04:00
u8 * pos ;
2010-01-17 03:47:58 +03:00
if ( info - > control . hw_key & &
! ( info - > control . hw_key - > flags & IEEE80211_KEY_FLAG_GENERATE_IV ) ) {
/* hwaccel - with no need for software-generated IV */
2008-05-29 12:38:53 +04:00
return 0 ;
2008-05-15 14:55:29 +04:00
}
2008-06-12 01:21:58 +04:00
hdrlen = ieee80211_hdrlen ( hdr - > frame_control ) ;
2007-05-05 22:45:53 +04:00
len = skb - > len - hdrlen ;
2010-01-17 03:47:58 +03:00
if ( info - > control . hw_key )
2008-05-29 12:38:53 +04:00
tail = 0 ;
2007-08-29 01:01:55 +04:00
else
2008-05-29 12:38:53 +04:00
tail = TKIP_ICV_LEN ;
if ( WARN_ON ( skb_tailroom ( skb ) < tail | |
skb_headroom ( skb ) < TKIP_IV_LEN ) )
return - 1 ;
2007-05-05 22:45:53 +04:00
pos = skb_push ( skb , TKIP_IV_LEN ) ;
memmove ( pos , pos + TKIP_IV_LEN , hdrlen ) ;
pos + = hdrlen ;
/* Increase IV for the frame */
2008-05-15 03:26:19 +04:00
key - > u . tkip . tx . iv16 + + ;
if ( key - > u . tkip . tx . iv16 = = 0 )
key - > u . tkip . tx . iv32 + + ;
2007-05-05 22:45:53 +04:00
2010-01-17 03:47:58 +03:00
pos = ieee80211_tkip_add_iv ( pos , key , key - > u . tkip . tx . iv16 ) ;
2007-05-05 22:45:53 +04:00
2010-01-17 03:47:58 +03:00
/* hwaccel - with software IV */
if ( info - > control . hw_key )
2007-05-05 22:45:53 +04:00
return 0 ;
/* Add room for ICV */
skb_put ( skb , TKIP_ICV_LEN ) ;
hdr = ( struct ieee80211_hdr * ) skb - > data ;
ieee80211_tkip_encrypt_data ( tx - > local - > wep_tx_tfm ,
key , pos , len , hdr - > addr2 ) ;
return 0 ;
}
2008-01-31 21:48:20 +03:00
ieee80211_tx_result
2008-02-25 18:27:43 +03:00
ieee80211_crypto_tkip_encrypt ( struct ieee80211_tx_data * tx )
2007-05-05 22:45:53 +04:00
{
struct sk_buff * skb = tx - > skb ;
2008-02-25 18:27:43 +03:00
ieee80211_tx_set_protected ( tx ) ;
2007-05-05 22:45:53 +04:00
2009-03-23 19:28:35 +03:00
do {
if ( tkip_encrypt_skb ( tx , skb ) < 0 )
return TX_DROP ;
} while ( ( skb = skb - > next ) ) ;
2007-05-05 22:45:53 +04:00
2008-01-31 21:48:20 +03:00
return TX_CONTINUE ;
2007-05-05 22:45:53 +04:00
}
2008-01-31 21:48:20 +03:00
ieee80211_rx_result
2008-02-25 18:27:43 +03:00
ieee80211_crypto_tkip_decrypt ( struct ieee80211_rx_data * rx )
2007-05-05 22:45:53 +04:00
{
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) rx - > skb - > data ;
int hdrlen , res , hwaccel = 0 , wpa_test = 0 ;
struct ieee80211_key * key = rx - > key ;
struct sk_buff * skb = rx - > skb ;
2009-11-16 15:58:20 +03:00
struct ieee80211_rx_status * status = IEEE80211_SKB_RXCB ( skb ) ;
2007-05-05 22:45:53 +04:00
2008-06-12 01:21:58 +04:00
hdrlen = ieee80211_hdrlen ( hdr - > frame_control ) ;
2007-05-05 22:45:53 +04:00
2008-07-03 03:30:52 +04:00
if ( ! ieee80211_is_data ( hdr - > frame_control ) )
2008-01-31 21:48:20 +03:00
return RX_CONTINUE ;
2007-05-05 22:45:53 +04:00
if ( ! rx - > sta | | skb - > len - hdrlen < 12 )
2008-01-31 21:48:21 +03:00
return RX_DROP_UNUSABLE ;
2007-05-05 22:45:53 +04:00
2009-11-16 15:58:20 +03:00
if ( status - > flag & RX_FLAG_DECRYPTED ) {
if ( status - > flag & RX_FLAG_IV_STRIPPED ) {
2007-09-14 19:10:25 +04:00
/*
* Hardware took care of all processing , including
* replay protection , and stripped the ICV / IV so
* we cannot do any checks here .
*/
2008-01-31 21:48:20 +03:00
return RX_CONTINUE ;
2007-05-05 22:45:53 +04:00
}
/* let TKIP code verify IV, but skip decryption */
hwaccel = 1 ;
}
res = ieee80211_tkip_decrypt_data ( rx - > local - > wep_rx_tfm ,
key , skb - > data + hdrlen ,
2008-09-11 02:02:02 +04:00
skb - > len - hdrlen , rx - > sta - > sta . addr ,
2008-03-20 16:06:42 +03:00
hdr - > addr1 , hwaccel , rx - > queue ,
2008-02-25 18:27:43 +03:00
& rx - > tkip_iv32 ,
& rx - > tkip_iv16 ) ;
2008-06-30 17:10:46 +04:00
if ( res ! = TKIP_DECRYPT_OK | | wpa_test )
2008-01-31 21:48:21 +03:00
return RX_DROP_UNUSABLE ;
2007-05-05 22:45:53 +04:00
/* Trim ICV */
skb_trim ( skb , skb - > len - TKIP_ICV_LEN ) ;
/* Remove IV */
memmove ( skb - > data + TKIP_IV_LEN , skb - > data , hdrlen ) ;
skb_pull ( skb , TKIP_IV_LEN ) ;
2008-01-31 21:48:20 +03:00
return RX_CONTINUE ;
2007-05-05 22:45:53 +04:00
}
2008-07-03 03:30:52 +04:00
static void ccmp_special_blocks ( struct sk_buff * skb , u8 * pn , u8 * scratch ,
2007-05-05 22:45:53 +04:00
int encrypted )
{
2008-07-02 22:05:35 +04:00
__le16 mask_fc ;
2009-01-08 14:32:00 +03:00
int a4_included , mgmt ;
2008-07-02 22:05:35 +04:00
u8 qos_tid ;
2008-07-03 03:30:52 +04:00
u8 * b_0 , * aad ;
2008-07-02 22:05:35 +04:00
u16 data_len , len_a ;
unsigned int hdrlen ;
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2007-05-05 22:45:53 +04:00
2008-07-03 03:30:52 +04:00
b_0 = scratch + 3 * AES_BLOCK_LEN ;
aad = scratch + 4 * AES_BLOCK_LEN ;
2008-07-02 22:05:35 +04:00
/*
2009-01-08 14:32:00 +03:00
* Mask FC : zero subtype b4 b5 b6 ( if not mgmt )
2008-07-02 22:05:35 +04:00
* Retry , PwrMgt , MoreData ; set Protected
*/
2009-01-08 14:32:00 +03:00
mgmt = ieee80211_is_mgmt ( hdr - > frame_control ) ;
2008-07-02 22:05:35 +04:00
mask_fc = hdr - > frame_control ;
2009-01-08 14:32:00 +03:00
mask_fc & = ~ cpu_to_le16 ( IEEE80211_FCTL_RETRY |
2008-07-02 22:05:35 +04:00
IEEE80211_FCTL_PM | IEEE80211_FCTL_MOREDATA ) ;
2009-01-08 14:32:00 +03:00
if ( ! mgmt )
mask_fc & = ~ cpu_to_le16 ( 0x0070 ) ;
2008-07-02 22:05:35 +04:00
mask_fc | = cpu_to_le16 ( IEEE80211_FCTL_PROTECTED ) ;
hdrlen = ieee80211_hdrlen ( hdr - > frame_control ) ;
len_a = hdrlen - 2 ;
a4_included = ieee80211_has_a4 ( hdr - > frame_control ) ;
2007-05-05 22:45:53 +04:00
2008-07-02 22:05:35 +04:00
if ( ieee80211_is_data_qos ( hdr - > frame_control ) )
qos_tid = * ieee80211_get_qos_ctl ( hdr ) & IEEE80211_QOS_CTL_TID_MASK ;
else
qos_tid = 0 ;
data_len = skb - > len - hdrlen - CCMP_HDR_LEN ;
if ( encrypted )
data_len - = CCMP_MIC_LEN ;
/* First block, b_0 */
2007-05-05 22:45:53 +04:00
b_0 [ 0 ] = 0x59 ; /* flags: Adata: 1, M: 011, L: 001 */
2009-01-08 14:32:00 +03:00
/* Nonce: Nonce Flags | A2 | PN
* Nonce Flags : Priority ( b0 . . b3 ) | Management ( b4 ) | Reserved ( b5 . . b7 )
*/
b_0 [ 1 ] = qos_tid | ( mgmt < < 4 ) ;
2008-07-02 22:05:34 +04:00
memcpy ( & b_0 [ 2 ] , hdr - > addr2 , ETH_ALEN ) ;
2007-05-05 22:45:53 +04:00
memcpy ( & b_0 [ 8 ] , pn , CCMP_PN_LEN ) ;
/* l(m) */
2008-07-02 22:05:35 +04:00
put_unaligned_be16 ( data_len , & b_0 [ 14 ] ) ;
2007-05-05 22:45:53 +04:00
/* AAD (extra authenticate-only data) / masked 802.11 header
* FC | A1 | A2 | A3 | SC | [ A4 ] | [ QC ] */
2008-07-02 22:05:35 +04:00
put_unaligned_be16 ( len_a , & aad [ 0 ] ) ;
put_unaligned ( mask_fc , ( __le16 * ) & aad [ 2 ] ) ;
2008-07-02 22:05:34 +04:00
memcpy ( & aad [ 4 ] , & hdr - > addr1 , 3 * ETH_ALEN ) ;
2007-05-05 22:45:53 +04:00
/* Mask Seq#, leave Frag# */
aad [ 22 ] = * ( ( u8 * ) & hdr - > seq_ctrl ) & 0x0f ;
aad [ 23 ] = 0 ;
2008-07-02 22:05:35 +04:00
2007-05-05 22:45:53 +04:00
if ( a4_included ) {
2008-07-02 22:05:34 +04:00
memcpy ( & aad [ 24 ] , hdr - > addr4 , ETH_ALEN ) ;
2008-07-02 22:05:35 +04:00
aad [ 30 ] = qos_tid ;
2007-05-05 22:45:53 +04:00
aad [ 31 ] = 0 ;
2008-07-02 22:05:35 +04:00
} else {
2008-07-02 22:05:34 +04:00
memset ( & aad [ 24 ] , 0 , ETH_ALEN + IEEE80211_QOS_CTL_LEN ) ;
2008-07-02 22:05:35 +04:00
aad [ 24 ] = qos_tid ;
2007-05-05 22:45:53 +04:00
}
}
static inline void ccmp_pn2hdr ( u8 * hdr , u8 * pn , int key_id )
{
hdr [ 0 ] = pn [ 5 ] ;
hdr [ 1 ] = pn [ 4 ] ;
hdr [ 2 ] = 0 ;
hdr [ 3 ] = 0x20 | ( key_id < < 6 ) ;
hdr [ 4 ] = pn [ 3 ] ;
hdr [ 5 ] = pn [ 2 ] ;
hdr [ 6 ] = pn [ 1 ] ;
hdr [ 7 ] = pn [ 0 ] ;
}
2008-10-07 14:04:32 +04:00
static inline void ccmp_hdr2pn ( u8 * pn , u8 * hdr )
2007-05-05 22:45:53 +04:00
{
pn [ 0 ] = hdr [ 7 ] ;
pn [ 1 ] = hdr [ 6 ] ;
pn [ 2 ] = hdr [ 5 ] ;
pn [ 3 ] = hdr [ 4 ] ;
pn [ 4 ] = hdr [ 1 ] ;
pn [ 5 ] = hdr [ 0 ] ;
}
2008-05-15 14:55:29 +04:00
static int ccmp_encrypt_skb ( struct ieee80211_tx_data * tx , struct sk_buff * skb )
2007-05-05 22:45:53 +04:00
{
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
struct ieee80211_key * key = tx - > key ;
2008-05-15 14:55:29 +04:00
struct ieee80211_tx_info * info = IEEE80211_SKB_CB ( skb ) ;
2008-05-29 12:38:53 +04:00
int hdrlen , len , tail ;
2008-07-03 03:30:52 +04:00
u8 * pos , * pn ;
2007-05-05 22:45:53 +04:00
int i ;
2010-01-17 03:47:58 +03:00
if ( info - > control . hw_key & &
! ( info - > control . hw_key - > flags & IEEE80211_KEY_FLAG_GENERATE_IV ) ) {
/*
* hwaccel has no need for preallocated room for CCMP
* header or MIC fields
*/
2008-05-29 12:38:53 +04:00
return 0 ;
2008-05-15 14:55:29 +04:00
}
2008-06-12 01:21:58 +04:00
hdrlen = ieee80211_hdrlen ( hdr - > frame_control ) ;
2007-05-05 22:45:53 +04:00
len = skb - > len - hdrlen ;
2010-01-17 03:47:58 +03:00
if ( info - > control . hw_key )
2008-05-29 12:38:53 +04:00
tail = 0 ;
2007-08-29 01:01:55 +04:00
else
2008-05-29 12:38:53 +04:00
tail = CCMP_MIC_LEN ;
if ( WARN_ON ( skb_tailroom ( skb ) < tail | |
skb_headroom ( skb ) < CCMP_HDR_LEN ) )
return - 1 ;
2007-05-05 22:45:53 +04:00
pos = skb_push ( skb , CCMP_HDR_LEN ) ;
memmove ( pos , pos + CCMP_HDR_LEN , hdrlen ) ;
hdr = ( struct ieee80211_hdr * ) pos ;
pos + = hdrlen ;
/* PN = PN + 1 */
pn = key - > u . ccmp . tx_pn ;
for ( i = CCMP_PN_LEN - 1 ; i > = 0 ; i - - ) {
pn [ i ] + + ;
if ( pn [ i ] )
break ;
}
2007-08-29 01:01:54 +04:00
ccmp_pn2hdr ( pos , pn , key - > conf . keyidx ) ;
2007-05-05 22:45:53 +04:00
2010-01-17 03:47:58 +03:00
/* hwaccel - with software CCMP header */
if ( info - > control . hw_key )
2007-05-05 22:45:53 +04:00
return 0 ;
pos + = CCMP_HDR_LEN ;
2008-07-03 03:30:52 +04:00
ccmp_special_blocks ( skb , pn , key - > u . ccmp . tx_crypto_buf , 0 ) ;
ieee80211_aes_ccm_encrypt ( key - > u . ccmp . tfm , key - > u . ccmp . tx_crypto_buf , pos , len ,
2007-05-05 22:45:53 +04:00
pos , skb_put ( skb , CCMP_MIC_LEN ) ) ;
return 0 ;
}
2008-01-31 21:48:20 +03:00
ieee80211_tx_result
2008-02-25 18:27:43 +03:00
ieee80211_crypto_ccmp_encrypt ( struct ieee80211_tx_data * tx )
2007-05-05 22:45:53 +04:00
{
struct sk_buff * skb = tx - > skb ;
2008-02-25 18:27:43 +03:00
ieee80211_tx_set_protected ( tx ) ;
2007-05-05 22:45:53 +04:00
2009-03-23 19:28:35 +03:00
do {
if ( ccmp_encrypt_skb ( tx , skb ) < 0 )
return TX_DROP ;
} while ( ( skb = skb - > next ) ) ;
2007-05-05 22:45:53 +04:00
2008-01-31 21:48:20 +03:00
return TX_CONTINUE ;
2007-05-05 22:45:53 +04:00
}
2008-01-31 21:48:20 +03:00
ieee80211_rx_result
2008-02-25 18:27:43 +03:00
ieee80211_crypto_ccmp_decrypt ( struct ieee80211_rx_data * rx )
2007-05-05 22:45:53 +04:00
{
2008-07-03 03:30:52 +04:00
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) rx - > skb - > data ;
2007-05-05 22:45:53 +04:00
int hdrlen ;
struct ieee80211_key * key = rx - > key ;
struct sk_buff * skb = rx - > skb ;
2009-11-16 15:58:20 +03:00
struct ieee80211_rx_status * status = IEEE80211_SKB_RXCB ( skb ) ;
2007-05-05 22:45:53 +04:00
u8 pn [ CCMP_PN_LEN ] ;
int data_len ;
2008-06-12 01:21:58 +04:00
hdrlen = ieee80211_hdrlen ( hdr - > frame_control ) ;
2007-05-05 22:45:53 +04:00
2009-01-08 14:32:00 +03:00
if ( ! ieee80211_is_data ( hdr - > frame_control ) & &
! ieee80211_is_robust_mgmt_frame ( hdr ) )
2008-01-31 21:48:20 +03:00
return RX_CONTINUE ;
2007-05-05 22:45:53 +04:00
data_len = skb - > len - hdrlen - CCMP_HDR_LEN - CCMP_MIC_LEN ;
if ( ! rx - > sta | | data_len < 0 )
2008-01-31 21:48:21 +03:00
return RX_DROP_UNUSABLE ;
2007-05-05 22:45:53 +04:00
2009-11-16 15:58:20 +03:00
if ( ( status - > flag & RX_FLAG_DECRYPTED ) & &
( status - > flag & RX_FLAG_IV_STRIPPED ) )
2008-01-31 21:48:20 +03:00
return RX_CONTINUE ;
2007-05-05 22:45:53 +04:00
2008-10-07 14:04:32 +04:00
ccmp_hdr2pn ( pn , skb - > data + hdrlen ) ;
2007-05-05 22:45:53 +04:00
2008-02-25 18:27:43 +03:00
if ( memcmp ( pn , key - > u . ccmp . rx_pn [ rx - > queue ] , CCMP_PN_LEN ) < = 0 ) {
2007-05-05 22:45:53 +04:00
key - > u . ccmp . replays + + ;
2008-01-31 21:48:21 +03:00
return RX_DROP_UNUSABLE ;
2007-05-05 22:45:53 +04:00
}
2009-11-16 15:58:20 +03:00
if ( ! ( status - > flag & RX_FLAG_DECRYPTED ) ) {
2007-09-14 19:10:25 +04:00
/* hardware didn't decrypt/verify MIC */
2008-07-03 03:30:52 +04:00
ccmp_special_blocks ( skb , pn , key - > u . ccmp . rx_crypto_buf , 1 ) ;
2007-05-05 22:45:53 +04:00
if ( ieee80211_aes_ccm_decrypt (
2008-07-03 03:30:52 +04:00
key - > u . ccmp . tfm , key - > u . ccmp . rx_crypto_buf ,
2007-05-05 22:45:53 +04:00
skb - > data + hdrlen + CCMP_HDR_LEN , data_len ,
skb - > data + skb - > len - CCMP_MIC_LEN ,
2008-10-07 14:04:32 +04:00
skb - > data + hdrlen + CCMP_HDR_LEN ) )
2008-01-31 21:48:21 +03:00
return RX_DROP_UNUSABLE ;
2007-05-05 22:45:53 +04:00
}
2008-02-25 18:27:43 +03:00
memcpy ( key - > u . ccmp . rx_pn [ rx - > queue ] , pn , CCMP_PN_LEN ) ;
2007-05-05 22:45:53 +04:00
/* Remove CCMP header and MIC */
skb_trim ( skb , skb - > len - CCMP_MIC_LEN ) ;
memmove ( skb - > data + CCMP_HDR_LEN , skb - > data , hdrlen ) ;
skb_pull ( skb , CCMP_HDR_LEN ) ;
2008-01-31 21:48:20 +03:00
return RX_CONTINUE ;
2007-05-05 22:45:53 +04:00
}
2009-01-08 14:32:01 +03:00
static void bip_aad ( struct sk_buff * skb , u8 * aad )
{
/* BIP AAD: FC(masked) || A1 || A2 || A3 */
/* FC type/subtype */
aad [ 0 ] = skb - > data [ 0 ] ;
/* Mask FC Retry, PwrMgt, MoreData flags to zero */
aad [ 1 ] = skb - > data [ 1 ] & ~ ( BIT ( 4 ) | BIT ( 5 ) | BIT ( 6 ) ) ;
/* A1 || A2 || A3 */
memcpy ( aad + 2 , skb - > data + 4 , 3 * ETH_ALEN ) ;
}
static inline void bip_ipn_swap ( u8 * d , const u8 * s )
{
* d + + = s [ 5 ] ;
* d + + = s [ 4 ] ;
* d + + = s [ 3 ] ;
* d + + = s [ 2 ] ;
* d + + = s [ 1 ] ;
* d = s [ 0 ] ;
}
ieee80211_tx_result
ieee80211_crypto_aes_cmac_encrypt ( struct ieee80211_tx_data * tx )
{
struct sk_buff * skb = tx - > skb ;
struct ieee80211_tx_info * info = IEEE80211_SKB_CB ( skb ) ;
struct ieee80211_key * key = tx - > key ;
struct ieee80211_mmie * mmie ;
u8 * pn , aad [ 20 ] ;
int i ;
2010-01-17 03:47:58 +03:00
if ( info - > control . hw_key )
2009-01-08 14:32:01 +03:00
return 0 ;
if ( WARN_ON ( skb_tailroom ( skb ) < sizeof ( * mmie ) ) )
return TX_DROP ;
mmie = ( struct ieee80211_mmie * ) skb_put ( skb , sizeof ( * mmie ) ) ;
mmie - > element_id = WLAN_EID_MMIE ;
mmie - > length = sizeof ( * mmie ) - 2 ;
mmie - > key_id = cpu_to_le16 ( key - > conf . keyidx ) ;
/* PN = PN + 1 */
pn = key - > u . aes_cmac . tx_pn ;
for ( i = sizeof ( key - > u . aes_cmac . tx_pn ) - 1 ; i > = 0 ; i - - ) {
pn [ i ] + + ;
if ( pn [ i ] )
break ;
}
bip_ipn_swap ( mmie - > sequence_number , pn ) ;
bip_aad ( skb , aad ) ;
/*
* MIC = AES - 128 - CMAC ( IGTK , AAD | | Management Frame Body | | MMIE , 64 )
*/
ieee80211_aes_cmac ( key - > u . aes_cmac . tfm , key - > u . aes_cmac . tx_crypto_buf ,
aad , skb - > data + 24 , skb - > len - 24 , mmie - > mic ) ;
return TX_CONTINUE ;
}
ieee80211_rx_result
ieee80211_crypto_aes_cmac_decrypt ( struct ieee80211_rx_data * rx )
{
struct sk_buff * skb = rx - > skb ;
2009-11-16 15:58:20 +03:00
struct ieee80211_rx_status * status = IEEE80211_SKB_RXCB ( skb ) ;
2009-01-08 14:32:01 +03:00
struct ieee80211_key * key = rx - > key ;
struct ieee80211_mmie * mmie ;
u8 aad [ 20 ] , mic [ 8 ] , ipn [ 6 ] ;
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
if ( ! ieee80211_is_mgmt ( hdr - > frame_control ) )
return RX_CONTINUE ;
2009-11-16 15:58:20 +03:00
if ( ( status - > flag & RX_FLAG_DECRYPTED ) & &
( status - > flag & RX_FLAG_IV_STRIPPED ) )
2009-01-08 14:32:01 +03:00
return RX_CONTINUE ;
if ( skb - > len < 24 + sizeof ( * mmie ) )
return RX_DROP_UNUSABLE ;
mmie = ( struct ieee80211_mmie * )
( skb - > data + skb - > len - sizeof ( * mmie ) ) ;
if ( mmie - > element_id ! = WLAN_EID_MMIE | |
mmie - > length ! = sizeof ( * mmie ) - 2 )
return RX_DROP_UNUSABLE ; /* Invalid MMIE */
bip_ipn_swap ( ipn , mmie - > sequence_number ) ;
if ( memcmp ( ipn , key - > u . aes_cmac . rx_pn , 6 ) < = 0 ) {
key - > u . aes_cmac . replays + + ;
return RX_DROP_UNUSABLE ;
}
2009-11-16 15:58:20 +03:00
if ( ! ( status - > flag & RX_FLAG_DECRYPTED ) ) {
2009-01-08 14:32:01 +03:00
/* hardware didn't decrypt/verify MIC */
bip_aad ( skb , aad ) ;
ieee80211_aes_cmac ( key - > u . aes_cmac . tfm ,
key - > u . aes_cmac . rx_crypto_buf , aad ,
skb - > data + 24 , skb - > len - 24 , mic ) ;
if ( memcmp ( mic , mmie - > mic , sizeof ( mmie - > mic ) ) ! = 0 ) {
key - > u . aes_cmac . icverrors + + ;
return RX_DROP_UNUSABLE ;
}
}
memcpy ( key - > u . aes_cmac . rx_pn , ipn , 6 ) ;
/* Remove MMIE */
skb_trim ( skb , skb - > len - sizeof ( * mmie ) ) ;
return RX_CONTINUE ;
}