2007-05-05 22:45:53 +04:00
/*
* Software WEP encryption implementation
* Copyright 2002 , Jouni Malinen < jkmaline @ cc . hut . fi >
* Copyright 2003 , 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/random.h>
# include <linux/compiler.h>
# include <linux/crc32.h>
# include <linux/crypto.h>
# include <linux/err.h>
# include <linux/mm.h>
2007-10-23 22:42:11 +04:00
# include <linux/scatterlist.h>
2007-05-05 22:45:53 +04:00
# include <net/mac80211.h>
# include "ieee80211_i.h"
# include "wep.h"
int ieee80211_wep_init ( struct ieee80211_local * local )
{
/* start WEP IV from a random value */
get_random_bytes ( & local - > wep_iv , WEP_IV_LEN ) ;
local - > wep_tx_tfm = crypto_alloc_blkcipher ( " ecb(arc4) " , 0 ,
CRYPTO_ALG_ASYNC ) ;
if ( IS_ERR ( local - > wep_tx_tfm ) )
return - ENOMEM ;
local - > wep_rx_tfm = crypto_alloc_blkcipher ( " ecb(arc4) " , 0 ,
CRYPTO_ALG_ASYNC ) ;
if ( IS_ERR ( local - > wep_rx_tfm ) ) {
crypto_free_blkcipher ( local - > wep_tx_tfm ) ;
return - ENOMEM ;
}
return 0 ;
}
void ieee80211_wep_free ( struct ieee80211_local * local )
{
crypto_free_blkcipher ( local - > wep_tx_tfm ) ;
crypto_free_blkcipher ( local - > wep_rx_tfm ) ;
}
static inline int ieee80211_wep_weak_iv ( u32 iv , int keylen )
{
/* Fluhrer, Mantin, and Shamir have reported weaknesses in the
* key scheduling algorithm of RC4 . At least IVs ( KeyByte + 3 ,
* 0xff , N ) can be used to speedup attacks , so avoid using them . */
if ( ( iv & 0xff00 ) = = 0xff00 ) {
u8 B = ( iv > > 16 ) & 0xff ;
if ( B > = 3 & & B < 3 + keylen )
return 1 ;
}
return 0 ;
}
2007-09-26 17:19:40 +04:00
static void ieee80211_wep_get_iv ( struct ieee80211_local * local ,
struct ieee80211_key * key , u8 * iv )
2007-05-05 22:45:53 +04:00
{
local - > wep_iv + + ;
2007-08-29 01:01:54 +04:00
if ( ieee80211_wep_weak_iv ( local - > wep_iv , key - > conf . keylen ) )
2007-05-05 22:45:53 +04:00
local - > wep_iv + = 0x0100 ;
if ( ! iv )
return ;
* iv + + = ( local - > wep_iv > > 16 ) & 0xff ;
* iv + + = ( local - > wep_iv > > 8 ) & 0xff ;
* iv + + = local - > wep_iv & 0xff ;
2007-08-29 01:01:54 +04:00
* iv + + = key - > conf . keyidx < < 6 ;
2007-05-05 22:45:53 +04:00
}
2007-09-26 17:19:41 +04:00
static u8 * ieee80211_wep_add_iv ( struct ieee80211_local * local ,
struct sk_buff * skb ,
struct ieee80211_key * key )
2007-05-05 22:45:53 +04:00
{
2008-06-23 03:45:23 +04:00
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
unsigned int hdrlen ;
2007-05-05 22:45:53 +04:00
u8 * newhdr ;
2008-06-23 03:45:23 +04:00
hdr - > frame_control | = cpu_to_le16 ( IEEE80211_FCTL_PROTECTED ) ;
2007-05-05 22:45:53 +04:00
2008-05-29 12:38:53 +04:00
if ( WARN_ON ( skb_tailroom ( skb ) < WEP_ICV_LEN | |
skb_headroom ( skb ) < WEP_IV_LEN ) )
return NULL ;
2007-05-05 22:45:53 +04:00
2008-06-23 03:45:23 +04:00
hdrlen = ieee80211_hdrlen ( hdr - > frame_control ) ;
2007-05-05 22:45:53 +04:00
newhdr = skb_push ( skb , WEP_IV_LEN ) ;
memmove ( newhdr , newhdr + WEP_IV_LEN , hdrlen ) ;
ieee80211_wep_get_iv ( local , key , newhdr + hdrlen ) ;
return newhdr + hdrlen ;
}
2007-09-26 17:19:40 +04:00
static void ieee80211_wep_remove_iv ( struct ieee80211_local * local ,
struct sk_buff * skb ,
struct ieee80211_key * key )
2007-05-05 22:45:53 +04:00
{
2008-06-23 03:45:23 +04:00
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
unsigned int hdrlen ;
2007-05-05 22:45:53 +04:00
2008-06-23 03:45:23 +04:00
hdrlen = ieee80211_hdrlen ( hdr - > frame_control ) ;
2007-05-05 22:45:53 +04:00
memmove ( skb - > data + WEP_IV_LEN , skb - > data , hdrlen ) ;
skb_pull ( skb , WEP_IV_LEN ) ;
}
/* Perform WEP encryption using given key. data buffer must have tailroom
* for 4 - byte ICV . data_len must not include this ICV . Note : this function
* does _not_ add IV . data = RC4 ( data | CRC32 ( data ) ) */
void ieee80211_wep_encrypt_data ( struct crypto_blkcipher * tfm , u8 * rc4key ,
size_t klen , u8 * data , size_t data_len )
{
struct blkcipher_desc desc = { . tfm = tfm } ;
struct scatterlist sg ;
__le32 * icv ;
icv = ( __le32 * ) ( data + data_len ) ;
* icv = cpu_to_le32 ( ~ crc32_le ( ~ 0 , data , data_len ) ) ;
crypto_blkcipher_setkey ( tfm , rc4key , klen ) ;
2007-10-22 21:44:26 +04:00
sg_init_one ( & sg , data , data_len + WEP_ICV_LEN ) ;
2007-05-05 22:45:53 +04:00
crypto_blkcipher_encrypt ( & desc , & sg , & sg , sg . length ) ;
}
/* Perform WEP encryption on given skb. 4 bytes of extra space (IV) in the
* beginning of the buffer 4 bytes of extra space ( ICV ) in the end of the
* buffer will be added . Both IV and ICV will be transmitted , so the
* payload length increases with 8 bytes .
*
* WEP frame payload : IV + TX key idx , RC4 ( data ) , ICV = RC4 ( CRC32 ( data ) )
*/
int ieee80211_wep_encrypt ( struct ieee80211_local * local , struct sk_buff * skb ,
struct ieee80211_key * key )
{
u32 klen ;
u8 * rc4key , * iv ;
size_t len ;
2007-08-29 01:01:54 +04:00
if ( ! key | | key - > conf . alg ! = ALG_WEP )
2007-05-05 22:45:53 +04:00
return - 1 ;
2007-08-29 01:01:54 +04:00
klen = 3 + key - > conf . keylen ;
2007-05-05 22:45:53 +04:00
rc4key = kmalloc ( klen , GFP_ATOMIC ) ;
if ( ! rc4key )
return - 1 ;
iv = ieee80211_wep_add_iv ( local , skb , key ) ;
if ( ! iv ) {
kfree ( rc4key ) ;
return - 1 ;
}
len = skb - > len - ( iv + WEP_IV_LEN - skb - > data ) ;
/* Prepend 24-bit IV to RC4 key */
memcpy ( rc4key , iv , 3 ) ;
/* Copy rest of the WEP key (the secret part) */
2007-08-29 01:01:54 +04:00
memcpy ( rc4key + 3 , key - > conf . key , key - > conf . keylen ) ;
2007-05-05 22:45:53 +04:00
/* Add room for ICV */
skb_put ( skb , WEP_ICV_LEN ) ;
ieee80211_wep_encrypt_data ( local - > wep_tx_tfm , rc4key , klen ,
iv + WEP_IV_LEN , len ) ;
kfree ( rc4key ) ;
return 0 ;
}
/* Perform WEP decryption using given key. data buffer includes encrypted
* payload , including 4 - byte ICV , but _not_ IV . data_len must not include ICV .
* Return 0 on success and - 1 on ICV mismatch . */
int ieee80211_wep_decrypt_data ( struct crypto_blkcipher * tfm , u8 * rc4key ,
size_t klen , u8 * data , size_t data_len )
{
struct blkcipher_desc desc = { . tfm = tfm } ;
struct scatterlist sg ;
__le32 crc ;
crypto_blkcipher_setkey ( tfm , rc4key , klen ) ;
2007-10-22 21:44:26 +04:00
sg_init_one ( & sg , data , data_len + WEP_ICV_LEN ) ;
2007-05-05 22:45:53 +04:00
crypto_blkcipher_decrypt ( & desc , & sg , & sg , sg . length ) ;
crc = cpu_to_le32 ( ~ crc32_le ( ~ 0 , data , data_len ) ) ;
if ( memcmp ( & crc , data + data_len , WEP_ICV_LEN ) ! = 0 )
/* ICV mismatch */
return - 1 ;
return 0 ;
}
/* Perform WEP decryption on given skb. Buffer includes whole WEP part of
* the frame : IV ( 4 bytes ) , encrypted payload ( including SNAP header ) ,
* ICV ( 4 bytes ) . skb - > len includes both IV and ICV .
*
* Returns 0 if frame was decrypted successfully and ICV was correct and - 1 on
* failure . If frame is OK , IV and ICV will be removed , i . e . , decrypted payload
* is moved to the beginning of the skb and skb length will be reduced .
*/
int ieee80211_wep_decrypt ( struct ieee80211_local * local , struct sk_buff * skb ,
struct ieee80211_key * key )
{
u32 klen ;
u8 * rc4key ;
u8 keyidx ;
2008-06-23 03:45:23 +04:00
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
unsigned int hdrlen ;
2007-05-05 22:45:53 +04:00
size_t len ;
int ret = 0 ;
2008-06-23 03:45:23 +04:00
if ( ! ieee80211_has_protected ( hdr - > frame_control ) )
2007-05-05 22:45:53 +04:00
return - 1 ;
2008-06-23 03:45:23 +04:00
hdrlen = ieee80211_hdrlen ( hdr - > frame_control ) ;
2007-05-05 22:45:53 +04:00
if ( skb - > len < 8 + hdrlen )
return - 1 ;
len = skb - > len - hdrlen - 8 ;
keyidx = skb - > data [ hdrlen + 3 ] > > 6 ;
2007-08-29 01:01:54 +04:00
if ( ! key | | keyidx ! = key - > conf . keyidx | | key - > conf . alg ! = ALG_WEP )
2007-05-05 22:45:53 +04:00
return - 1 ;
2007-08-29 01:01:54 +04:00
klen = 3 + key - > conf . keylen ;
2007-05-05 22:45:53 +04:00
rc4key = kmalloc ( klen , GFP_ATOMIC ) ;
if ( ! rc4key )
return - 1 ;
/* Prepend 24-bit IV to RC4 key */
memcpy ( rc4key , skb - > data + hdrlen , 3 ) ;
/* Copy rest of the WEP key (the secret part) */
2007-08-29 01:01:54 +04:00
memcpy ( rc4key + 3 , key - > conf . key , key - > conf . keylen ) ;
2007-05-05 22:45:53 +04:00
if ( ieee80211_wep_decrypt_data ( local - > wep_rx_tfm , rc4key , klen ,
skb - > data + hdrlen + WEP_IV_LEN ,
len ) ) {
2007-11-29 19:09:41 +03:00
if ( net_ratelimit ( ) )
printk ( KERN_DEBUG " WEP decrypt failed (ICV) \n " ) ;
2007-05-05 22:45:53 +04:00
ret = - 1 ;
}
kfree ( rc4key ) ;
/* Trim ICV */
skb_trim ( skb , skb - > len - WEP_ICV_LEN ) ;
/* Remove IV */
memmove ( skb - > data + WEP_IV_LEN , skb - > data , hdrlen ) ;
skb_pull ( skb , WEP_IV_LEN ) ;
return ret ;
}
u8 * ieee80211_wep_is_weak_iv ( struct sk_buff * skb , struct ieee80211_key * key )
{
2008-06-23 03:45:23 +04:00
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
unsigned int hdrlen ;
2007-05-05 22:45:53 +04:00
u8 * ivpos ;
u32 iv ;
2008-06-23 03:45:23 +04:00
if ( ! ieee80211_has_protected ( hdr - > frame_control ) )
2007-05-05 22:45:53 +04:00
return NULL ;
2008-06-23 03:45:23 +04:00
hdrlen = ieee80211_hdrlen ( hdr - > frame_control ) ;
2007-05-05 22:45:53 +04:00
ivpos = skb - > data + hdrlen ;
iv = ( ivpos [ 0 ] < < 16 ) | ( ivpos [ 1 ] < < 8 ) | ivpos [ 2 ] ;
2007-08-29 01:01:54 +04:00
if ( ieee80211_wep_weak_iv ( iv , key - > conf . keylen ) )
2007-05-05 22:45:53 +04:00
return ivpos ;
return NULL ;
}
2007-09-26 17:19:40 +04:00
2008-01-31 21:48:20 +03:00
ieee80211_rx_result
2008-02-25 18:27:43 +03:00
ieee80211_crypto_wep_decrypt ( struct ieee80211_rx_data * rx )
2007-09-26 17:19:40 +04:00
{
if ( ( rx - > fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_DATA & &
( ( rx - > fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_MGMT | |
( rx - > fc & IEEE80211_FCTL_STYPE ) ! = IEEE80211_STYPE_AUTH ) )
2008-01-31 21:48:20 +03:00
return RX_CONTINUE ;
2007-09-26 17:19:40 +04:00
2008-02-25 18:27:43 +03:00
if ( ! ( rx - > status - > flag & RX_FLAG_DECRYPTED ) ) {
2007-09-26 17:19:40 +04:00
if ( ieee80211_wep_decrypt ( rx - > local , rx - > skb , rx - > key ) ) {
2007-11-07 01:12:31 +03:00
# ifdef CONFIG_MAC80211_DEBUG
2007-09-26 17:19:40 +04:00
if ( net_ratelimit ( ) )
printk ( KERN_DEBUG " %s: RX WEP frame, decrypt "
" failed \n " , rx - > dev - > name ) ;
2007-11-07 01:12:31 +03:00
# endif /* CONFIG_MAC80211_DEBUG */
2008-01-31 21:48:21 +03:00
return RX_DROP_UNUSABLE ;
2007-09-26 17:19:40 +04:00
}
2008-02-25 18:27:43 +03:00
} else if ( ! ( rx - > status - > flag & RX_FLAG_IV_STRIPPED ) ) {
2007-09-26 17:19:40 +04:00
ieee80211_wep_remove_iv ( rx - > local , rx - > skb , rx - > key ) ;
/* remove ICV */
skb_trim ( rx - > skb , rx - > skb - > len - 4 ) ;
}
2008-01-31 21:48:20 +03:00
return RX_CONTINUE ;
2007-09-26 17:19:40 +04:00
}
2007-09-26 17:19:41 +04:00
2008-02-25 18:27:43 +03:00
static int wep_encrypt_skb ( struct ieee80211_tx_data * tx , struct sk_buff * skb )
2007-09-26 17:19:41 +04:00
{
2008-05-15 14:55:29 +04:00
struct ieee80211_tx_info * info = IEEE80211_SKB_CB ( skb ) ;
info - > control . iv_len = WEP_IV_LEN ;
info - > control . icv_len = WEP_ICV_LEN ;
2007-09-26 17:19:41 +04:00
if ( ! ( tx - > key - > flags & KEY_FLAG_UPLOADED_TO_HARDWARE ) ) {
if ( ieee80211_wep_encrypt ( tx - > local , skb , tx - > key ) )
return - 1 ;
} else {
2008-06-02 15:54:50 +04:00
info - > control . hw_key = & tx - > key - > conf ;
2007-09-26 17:19:41 +04:00
if ( tx - > key - > conf . flags & IEEE80211_KEY_FLAG_GENERATE_IV ) {
if ( ! ieee80211_wep_add_iv ( tx - > local , skb , tx - > key ) )
return - 1 ;
}
}
return 0 ;
}
2008-01-31 21:48:20 +03:00
ieee80211_tx_result
2008-02-25 18:27:43 +03:00
ieee80211_crypto_wep_encrypt ( struct ieee80211_tx_data * tx )
2007-09-26 17:19:41 +04:00
{
2008-02-25 18:27:43 +03:00
ieee80211_tx_set_protected ( tx ) ;
2007-09-26 17:19:41 +04:00
if ( wep_encrypt_skb ( tx , tx - > skb ) < 0 ) {
I802_DEBUG_INC ( tx - > local - > tx_handlers_drop_wep ) ;
2008-01-31 21:48:20 +03:00
return TX_DROP ;
2007-09-26 17:19:41 +04:00
}
2008-02-25 18:27:43 +03:00
if ( tx - > extra_frag ) {
2007-09-26 17:19:41 +04:00
int i ;
2008-02-25 18:27:43 +03:00
for ( i = 0 ; i < tx - > num_extra_frag ; i + + ) {
if ( wep_encrypt_skb ( tx , tx - > extra_frag [ i ] ) < 0 ) {
2007-09-26 17:19:41 +04:00
I802_DEBUG_INC ( tx - > local - >
tx_handlers_drop_wep ) ;
2008-01-31 21:48:20 +03:00
return TX_DROP ;
2007-09-26 17:19:41 +04:00
}
}
}
2008-01-31 21:48:20 +03:00
return TX_CONTINUE ;
2007-09-26 17:19:41 +04:00
}