2007-05-05 22:45:53 +04:00
/*
* Copyright 2002 - 2004 , Instant802 Networks , Inc .
*
* 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>
# 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"
# include "wpa.h"
static int ieee80211_get_hdr_info ( const struct sk_buff * skb , u8 * * sa , u8 * * da ,
u8 * qos_tid , u8 * * data , size_t * data_len )
{
struct ieee80211_hdr * hdr ;
size_t hdrlen ;
2008-06-12 01:21:58 +04:00
__le16 fc ;
2007-05-05 22:45:53 +04:00
2008-06-12 01:21:58 +04:00
hdr = ( struct ieee80211_hdr * ) skb - > data ;
fc = hdr - > frame_control ;
2007-05-05 22:45:53 +04:00
2008-06-12 01:21:58 +04:00
hdrlen = ieee80211_hdrlen ( fc ) ;
* sa = ieee80211_get_SA ( hdr ) ;
* da = ieee80211_get_DA ( hdr ) ;
2007-05-05 22:45:53 +04:00
* data = skb - > data + hdrlen ;
* data_len = skb - > len - hdrlen ;
2008-06-12 01:21:58 +04:00
if ( ieee80211_is_data_qos ( fc ) )
* qos_tid = ( * ieee80211_get_qos_ctl ( hdr ) & 0x0f ) | 0x80 ;
else
2007-05-05 22:45:53 +04:00
* qos_tid = 0 ;
return skb - > len < hdrlen ? - 1 : 0 ;
}
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
{
u8 * data , * sa , * da , * key , * mic , qos_tid ;
size_t data_len ;
u16 fc ;
struct sk_buff * skb = tx - > skb ;
int authenticator ;
int wpa_test = 0 ;
2008-05-29 12:38:53 +04:00
int tail ;
2007-05-05 22:45:53 +04:00
fc = tx - > fc ;
2007-08-29 01:01:54 +04:00
if ( ! tx - > key | | tx - > key - > conf . alg ! = ALG_TKIP | | skb - > len < 24 | |
2007-05-05 22:45:53 +04:00
! WLAN_FC_DATA_PRESENT ( fc ) )
2008-01-31 21:48:20 +03:00
return TX_CONTINUE ;
2007-05-05 22:45:53 +04:00
if ( ieee80211_get_hdr_info ( skb , & sa , & da , & qos_tid , & data , & data_len ) )
2008-01-31 21:48:20 +03:00
return TX_DROP ;
2007-05-05 22:45:53 +04:00
2007-08-29 01:01:55 +04:00
if ( ( tx - > key - > flags & KEY_FLAG_UPLOADED_TO_HARDWARE ) & &
2008-02-25 18:27:43 +03:00
! ( tx - > flags & IEEE80211_TX_FRAGMENTED ) & &
2007-09-14 19:10:25 +04:00
! ( tx - > key - > conf . flags & IEEE80211_KEY_FLAG_GENERATE_MMIC ) & &
2007-05-05 22:45:53 +04:00
! wpa_test ) {
/* hwaccel - with no need for preallocated room for Michael MIC
*/
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 ;
if ( ! ( tx - > key - > flags & KEY_FLAG_UPLOADED_TO_HARDWARE ) )
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
2007-08-29 01:01:54 +04:00
key = & tx - > key - > conf . key [ authenticator ? ALG_TKIP_TEMP_AUTH_TX_MIC_KEY :
ALG_TKIP_TEMP_AUTH_RX_MIC_KEY ] ;
2007-05-05 22:45:53 +04:00
mic = skb_put ( skb , MICHAEL_MIC_LEN ) ;
michael_mic ( key , da , sa , qos_tid & 0x0f , data , data_len , mic ) ;
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
{
u8 * data , * sa , * da , * key = NULL , qos_tid ;
size_t data_len ;
u16 fc ;
u8 mic [ MICHAEL_MIC_LEN ] ;
struct sk_buff * skb = rx - > skb ;
int authenticator = 1 , wpa_test = 0 ;
2007-10-04 04:59:30 +04:00
DECLARE_MAC_BUF ( mac ) ;
2007-05-05 22:45:53 +04:00
fc = rx - > fc ;
2007-08-29 01:01:53 +04:00
/*
* No way to verify the MIC if the hardware stripped it
*/
2008-02-25 18:27:43 +03:00
if ( rx - > 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 | |
2007-05-05 22:45:53 +04:00
! ( rx - > fc & IEEE80211_FCTL_PROTECTED ) | | ! WLAN_FC_DATA_PRESENT ( fc ) )
2008-01-31 21:48:20 +03:00
return RX_CONTINUE ;
2007-05-05 22:45:53 +04:00
if ( ieee80211_get_hdr_info ( skb , & sa , & da , & qos_tid , & data , & data_len )
| | data_len < MICHAEL_MIC_LEN )
2008-01-31 21:48:21 +03:00
return RX_DROP_UNUSABLE ;
2007-05-05 22:45:53 +04:00
data_len - = MICHAEL_MIC_LEN ;
#if 0
authenticator = fc & IEEE80211_FCTL_TODS ; /* FIX */
# else
authenticator = 1 ;
# endif
2007-08-29 01:01:54 +04:00
key = & rx - > key - > conf . key [ authenticator ? ALG_TKIP_TEMP_AUTH_RX_MIC_KEY :
ALG_TKIP_TEMP_AUTH_TX_MIC_KEY ] ;
2007-05-05 22:45:53 +04:00
michael_mic ( key , da , sa , qos_tid & 0x0f , data , data_len , mic ) ;
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
printk ( KERN_DEBUG " %s: invalid Michael MIC in data frame from "
2007-10-04 04:59:30 +04:00
" %s \n " , rx - > dev - > name , print_mac ( mac , sa ) ) ;
2007-05-05 22:45:53 +04:00
2007-08-29 01:01:54 +04:00
mac80211_ev_michael_mic_failure ( rx - > dev , rx - > key - > conf . keyidx ,
2007-08-29 01:01:53 +04:00
( void * ) skb - > data ) ;
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 ;
2008-05-15 14:55:29 +04:00
info - > control . icv_len = TKIP_ICV_LEN ;
info - > control . iv_len = TKIP_IV_LEN ;
if ( ( tx - > key - > flags & KEY_FLAG_UPLOADED_TO_HARDWARE ) & &
! ( tx - > key - > conf . flags & IEEE80211_KEY_FLAG_GENERATE_IV ) ) {
/* hwaccel - with no need for preallocated room for IV/ICV */
info - > control . hw_key = & tx - > key - > conf ;
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 ;
2007-08-29 01:01:55 +04:00
if ( tx - > key - > flags & KEY_FLAG_UPLOADED_TO_HARDWARE )
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
2007-08-29 01:01:55 +04:00
if ( tx - > key - > flags & KEY_FLAG_UPLOADED_TO_HARDWARE ) {
2007-05-05 22:45:53 +04:00
/* hwaccel - with preallocated room for IV */
2008-06-12 01:22:00 +04:00
ieee80211_tkip_add_iv ( pos , key , key - > u . tkip . tx . iv16 ) ;
2007-05-05 22:45:53 +04:00
2008-05-15 14:55:29 +04:00
info - > control . hw_key = & tx - > key - > conf ;
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
2008-05-15 14:55:29 +04:00
if ( tkip_encrypt_skb ( tx , skb ) < 0 )
2008-01-31 21:48:20 +03:00
return TX_DROP ;
2007-05-05 22:45:53 +04:00
2008-02-25 18:27:43 +03:00
if ( tx - > extra_frag ) {
2007-05-05 22:45:53 +04:00
int i ;
2008-02-25 18:27:43 +03:00
for ( i = 0 ; i < tx - > num_extra_frag ; i + + ) {
2008-05-15 14:55:29 +04:00
if ( tkip_encrypt_skb ( tx , tx - > extra_frag [ i ] ) < 0 )
2008-01-31 21:48:20 +03:00
return TX_DROP ;
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 ;
2007-10-04 04:59:30 +04:00
DECLARE_MAC_BUF ( mac ) ;
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
2007-09-26 17:19:40 +04:00
if ( ( rx - > fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_DATA )
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
2008-02-25 18:27:43 +03:00
if ( rx - > status - > flag & RX_FLAG_DECRYPTED ) {
if ( rx - > 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 ,
skb - > len - hdrlen , rx - > 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 ) ;
2007-05-05 22:45:53 +04:00
if ( res ! = TKIP_DECRYPT_OK | | wpa_test ) {
2007-11-07 01:12:31 +03:00
# ifdef CONFIG_MAC80211_DEBUG
if ( net_ratelimit ( ) )
printk ( KERN_DEBUG " %s: TKIP decrypt failed for RX "
" frame from %s (res=%d) \n " , rx - > dev - > name ,
print_mac ( mac , rx - > sta - > addr ) , res ) ;
# endif /* CONFIG_MAC80211_DEBUG */
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
}
static void ccmp_special_blocks ( struct sk_buff * skb , u8 * pn , u8 * b_0 , u8 * aad ,
int encrypted )
{
u16 fc ;
int a4_included , qos_included ;
u8 qos_tid , * fc_pos , * data , * sa , * da ;
int len_a ;
size_t data_len ;
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
fc_pos = ( u8 * ) & hdr - > frame_control ;
fc = fc_pos [ 0 ] ^ ( fc_pos [ 1 ] < < 8 ) ;
a4_included = ( fc & ( IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS ) ) = =
( IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS ) ;
ieee80211_get_hdr_info ( skb , & sa , & da , & qos_tid , & data , & data_len ) ;
data_len - = CCMP_HDR_LEN + ( encrypted ? CCMP_MIC_LEN : 0 ) ;
if ( qos_tid & 0x80 ) {
qos_included = 1 ;
qos_tid & = 0x0f ;
} else
qos_included = 0 ;
/* First block, b_0 */
b_0 [ 0 ] = 0x59 ; /* flags: Adata: 1, M: 011, L: 001 */
/* Nonce: QoS Priority | A2 | PN */
b_0 [ 1 ] = qos_tid ;
memcpy ( & b_0 [ 2 ] , hdr - > addr2 , 6 ) ;
memcpy ( & b_0 [ 8 ] , pn , CCMP_PN_LEN ) ;
/* l(m) */
b_0 [ 14 ] = ( data_len > > 8 ) & 0xff ;
b_0 [ 15 ] = data_len & 0xff ;
/* AAD (extra authenticate-only data) / masked 802.11 header
* FC | A1 | A2 | A3 | SC | [ A4 ] | [ QC ] */
len_a = a4_included ? 28 : 22 ;
if ( qos_included )
len_a + = 2 ;
aad [ 0 ] = 0 ; /* (len_a >> 8) & 0xff; */
aad [ 1 ] = len_a & 0xff ;
/* Mask FC: zero subtype b4 b5 b6 */
aad [ 2 ] = fc_pos [ 0 ] & ~ ( BIT ( 4 ) | BIT ( 5 ) | BIT ( 6 ) ) ;
/* Retry, PwrMgt, MoreData; set Protected */
aad [ 3 ] = ( fc_pos [ 1 ] & ~ ( BIT ( 3 ) | BIT ( 4 ) | BIT ( 5 ) ) ) | BIT ( 6 ) ;
memcpy ( & aad [ 4 ] , & hdr - > addr1 , 18 ) ;
/* Mask Seq#, leave Frag# */
aad [ 22 ] = * ( ( u8 * ) & hdr - > seq_ctrl ) & 0x0f ;
aad [ 23 ] = 0 ;
if ( a4_included ) {
memcpy ( & aad [ 24 ] , hdr - > addr4 , 6 ) ;
aad [ 30 ] = 0 ;
aad [ 31 ] = 0 ;
} else
memset ( & aad [ 24 ] , 0 , 8 ) ;
if ( qos_included ) {
u8 * dpos = & aad [ a4_included ? 30 : 24 ] ;
/* Mask QoS Control field */
dpos [ 0 ] = qos_tid ;
dpos [ 1 ] = 0 ;
}
}
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 ] ;
}
static inline int ccmp_hdr2pn ( u8 * pn , u8 * hdr )
{
pn [ 0 ] = hdr [ 7 ] ;
pn [ 1 ] = hdr [ 6 ] ;
pn [ 2 ] = hdr [ 5 ] ;
pn [ 3 ] = hdr [ 4 ] ;
pn [ 4 ] = hdr [ 1 ] ;
pn [ 5 ] = hdr [ 0 ] ;
return ( hdr [ 3 ] > > 6 ) & 0x03 ;
}
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 ;
2007-05-05 22:45:53 +04:00
u8 * pos , * pn , * b_0 , * aad , * scratch ;
int i ;
2008-05-15 14:55:29 +04:00
info - > control . icv_len = CCMP_MIC_LEN ;
info - > control . iv_len = CCMP_HDR_LEN ;
if ( ( tx - > key - > flags & KEY_FLAG_UPLOADED_TO_HARDWARE ) & &
! ( tx - > key - > conf . flags & IEEE80211_KEY_FLAG_GENERATE_IV ) ) {
/* hwaccel - with no need for preallocated room for CCMP "
* header or MIC fields */
info - > control . hw_key = & tx - > key - > conf ;
2008-05-29 12:38:53 +04:00
return 0 ;
2008-05-15 14:55:29 +04:00
}
2007-05-05 22:45:53 +04:00
scratch = key - > u . ccmp . tx_crypto_buf ;
b_0 = scratch + 3 * AES_BLOCK_LEN ;
aad = scratch + 4 * AES_BLOCK_LEN ;
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 ;
2007-08-29 01:01:55 +04:00
if ( key - > flags & KEY_FLAG_UPLOADED_TO_HARDWARE )
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
2007-08-29 01:01:55 +04:00
if ( key - > flags & KEY_FLAG_UPLOADED_TO_HARDWARE ) {
2007-05-05 22:45:53 +04:00
/* hwaccel - with preallocated room for CCMP header */
2008-05-15 14:55:29 +04:00
info - > control . hw_key = & tx - > key - > conf ;
2007-05-05 22:45:53 +04:00
return 0 ;
}
pos + = CCMP_HDR_LEN ;
ccmp_special_blocks ( skb , pn , b_0 , aad , 0 ) ;
ieee80211_aes_ccm_encrypt ( key - > u . ccmp . tfm , scratch , b_0 , aad , pos , len ,
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
2008-05-15 14:55:29 +04:00
if ( ccmp_encrypt_skb ( tx , skb ) < 0 )
2008-01-31 21:48:20 +03:00
return TX_DROP ;
2007-05-05 22:45:53 +04:00
2008-02-25 18:27:43 +03:00
if ( tx - > extra_frag ) {
2007-05-05 22:45:53 +04:00
int i ;
2008-02-25 18:27:43 +03:00
for ( i = 0 ; i < tx - > num_extra_frag ; i + + ) {
2008-05-15 14:55:29 +04:00
if ( ccmp_encrypt_skb ( tx , tx - > extra_frag [ i ] ) < 0 )
2008-01-31 21:48:20 +03:00
return TX_DROP ;
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
{
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) rx - > skb - > data ;
int hdrlen ;
struct ieee80211_key * key = rx - > key ;
struct sk_buff * skb = rx - > skb ;
u8 pn [ CCMP_PN_LEN ] ;
int data_len ;
2007-10-04 04:59:30 +04:00
DECLARE_MAC_BUF ( mac ) ;
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
2007-09-26 17:19:40 +04:00
if ( ( rx - > fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_DATA )
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
2008-02-25 18:27:43 +03:00
if ( ( rx - > status - > flag & RX_FLAG_DECRYPTED ) & &
( rx - > 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
( void ) ccmp_hdr2pn ( pn , skb - > data + hdrlen ) ;
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
# ifdef CONFIG_MAC80211_DEBUG
2008-02-25 18:27:43 +03:00
u8 * ppn = key - > u . ccmp . rx_pn [ rx - > queue ] ;
2007-10-04 04:59:30 +04:00
2007-05-05 22:45:53 +04:00
printk ( KERN_DEBUG " %s: CCMP replay detected for RX frame from "
2007-10-04 04:59:30 +04:00
" %s (RX PN %02x%02x%02x%02x%02x%02x <= prev. PN "
2007-05-05 22:45:53 +04:00
" %02x%02x%02x%02x%02x%02x) \n " , rx - > dev - > name ,
2007-10-04 04:59:30 +04:00
print_mac ( mac , rx - > sta - > addr ) ,
2007-05-05 22:45:53 +04:00
pn [ 0 ] , pn [ 1 ] , pn [ 2 ] , pn [ 3 ] , pn [ 4 ] , pn [ 5 ] ,
ppn [ 0 ] , ppn [ 1 ] , ppn [ 2 ] , ppn [ 3 ] , ppn [ 4 ] , ppn [ 5 ] ) ;
# endif /* CONFIG_MAC80211_DEBUG */
key - > u . ccmp . replays + + ;
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
if ( ! ( rx - > status - > flag & RX_FLAG_DECRYPTED ) ) {
2007-09-14 19:10:25 +04:00
/* hardware didn't decrypt/verify MIC */
2007-05-05 22:45:53 +04:00
u8 * scratch , * b_0 , * aad ;
scratch = key - > u . ccmp . rx_crypto_buf ;
b_0 = scratch + 3 * AES_BLOCK_LEN ;
aad = scratch + 4 * AES_BLOCK_LEN ;
ccmp_special_blocks ( skb , pn , b_0 , aad , 1 ) ;
if ( ieee80211_aes_ccm_decrypt (
key - > u . ccmp . tfm , scratch , b_0 , aad ,
skb - > data + hdrlen + CCMP_HDR_LEN , data_len ,
skb - > data + skb - > len - CCMP_MIC_LEN ,
skb - > data + hdrlen + CCMP_HDR_LEN ) ) {
2007-11-07 01:12:31 +03:00
# ifdef CONFIG_MAC80211_DEBUG
if ( net_ratelimit ( ) )
printk ( KERN_DEBUG " %s: CCMP decrypt failed "
" for RX frame from %s \n " , rx - > dev - > name ,
print_mac ( mac , rx - > sta - > addr ) ) ;
# endif /* CONFIG_MAC80211_DEBUG */
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
}