2005-05-13 06:48:20 +04:00
/*
2008-10-29 18:35:05 +03:00
* lib80211 crypt : host - based WEP encryption implementation for lib80211
2005-05-13 06:48:20 +04:00
*
2007-03-25 04:15:30 +04:00
* Copyright ( c ) 2002 - 2004 , Jouni Malinen < j @ w1 . fi >
2008-10-29 18:35:05 +03:00
* Copyright ( c ) 2008 , John W . Linville < linville @ tuxdriver . com >
2005-05-13 06:48:20 +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 . See README and COPYING for
* more details .
*/
2006-08-22 14:36:13 +04:00
# include <linux/err.h>
2005-05-13 06:48:20 +04:00
# include <linux/module.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/random.h>
2007-10-23 22:42:11 +04:00
# include <linux/scatterlist.h>
2005-05-13 06:48:20 +04:00
# include <linux/skbuff.h>
2006-12-04 07:15:30 +03:00
# include <linux/mm.h>
2005-05-13 06:48:20 +04:00
# include <asm/string.h>
2008-10-29 18:35:05 +03:00
# include <net/lib80211.h>
2005-05-13 06:48:20 +04:00
# include <linux/crypto.h>
# include <linux/crc32.h>
MODULE_AUTHOR ( " Jouni Malinen " ) ;
2008-10-29 18:35:05 +03:00
MODULE_DESCRIPTION ( " lib80211 crypt: WEP " ) ;
2005-05-13 06:48:20 +04:00
MODULE_LICENSE ( " GPL " ) ;
2008-10-29 18:35:05 +03:00
struct lib80211_wep_data {
2005-05-13 06:48:20 +04:00
u32 iv ;
# define WEP_KEY_LEN 13
u8 key [ WEP_KEY_LEN + 1 ] ;
u8 key_len ;
u8 key_idx ;
2006-09-23 04:10:23 +04:00
struct crypto_blkcipher * tx_tfm ;
struct crypto_blkcipher * rx_tfm ;
2005-05-13 06:48:20 +04:00
} ;
2008-10-29 18:35:05 +03:00
static void * lib80211_wep_init ( int keyidx )
2005-05-13 06:48:20 +04:00
{
2008-10-29 18:35:05 +03:00
struct lib80211_wep_data * priv ;
2005-05-13 06:48:20 +04:00
2006-07-22 01:51:30 +04:00
priv = kzalloc ( sizeof ( * priv ) , GFP_ATOMIC ) ;
2005-05-13 06:48:20 +04:00
if ( priv = = NULL )
goto fail ;
priv - > key_idx = keyidx ;
2006-09-23 04:10:23 +04:00
priv - > tx_tfm = crypto_alloc_blkcipher ( " ecb(arc4) " , 0 , CRYPTO_ALG_ASYNC ) ;
if ( IS_ERR ( priv - > tx_tfm ) ) {
2006-09-23 05:19:05 +04:00
priv - > tx_tfm = NULL ;
2005-05-13 06:48:20 +04:00
goto fail ;
}
2006-09-23 04:10:23 +04:00
priv - > rx_tfm = crypto_alloc_blkcipher ( " ecb(arc4) " , 0 , CRYPTO_ALG_ASYNC ) ;
if ( IS_ERR ( priv - > rx_tfm ) ) {
2006-09-23 05:19:05 +04:00
priv - > rx_tfm = NULL ;
2006-08-21 07:33:56 +04:00
goto fail ;
}
2005-05-13 06:48:20 +04:00
/* start WEP IV from a random value */
get_random_bytes ( & priv - > iv , 4 ) ;
return priv ;
2005-09-07 08:48:31 +04:00
fail :
2005-05-13 06:48:20 +04:00
if ( priv ) {
2006-08-21 07:33:56 +04:00
if ( priv - > tx_tfm )
2006-09-23 04:10:23 +04:00
crypto_free_blkcipher ( priv - > tx_tfm ) ;
2006-08-21 07:33:56 +04:00
if ( priv - > rx_tfm )
2006-09-23 04:10:23 +04:00
crypto_free_blkcipher ( priv - > rx_tfm ) ;
2005-05-13 06:48:20 +04:00
kfree ( priv ) ;
}
return NULL ;
}
2008-10-29 18:35:05 +03:00
static void lib80211_wep_deinit ( void * priv )
2005-05-13 06:48:20 +04:00
{
2008-10-29 18:35:05 +03:00
struct lib80211_wep_data * _priv = priv ;
2006-08-21 07:33:56 +04:00
if ( _priv ) {
if ( _priv - > tx_tfm )
2006-09-23 04:10:23 +04:00
crypto_free_blkcipher ( _priv - > tx_tfm ) ;
2006-08-21 07:33:56 +04:00
if ( _priv - > rx_tfm )
2006-09-23 04:10:23 +04:00
crypto_free_blkcipher ( _priv - > rx_tfm ) ;
2006-08-21 07:33:56 +04:00
}
2005-05-13 06:48:20 +04:00
kfree ( priv ) ;
}
2005-12-31 13:35:20 +03:00
/* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */
2008-10-29 18:35:05 +03:00
static int lib80211_wep_build_iv ( struct sk_buff * skb , int hdr_len ,
2006-01-19 11:22:32 +03:00
u8 * key , int keylen , void * priv )
2005-05-13 06:48:20 +04:00
{
2008-10-29 18:35:05 +03:00
struct lib80211_wep_data * wep = priv ;
2011-04-25 14:26:16 +04:00
u32 klen ;
2005-12-31 13:35:20 +03:00
u8 * pos ;
2007-02-09 17:24:46 +03:00
2005-12-31 13:35:20 +03:00
if ( skb_headroom ( skb ) < 4 | | skb - > len < hdr_len )
2005-05-13 06:48:20 +04:00
return - 1 ;
pos = skb_push ( skb , 4 ) ;
memmove ( pos , pos + 4 , hdr_len ) ;
pos + = hdr_len ;
klen = 3 + wep - > key_len ;
wep - > iv + + ;
/* 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 ( ( wep - > iv & 0xff00 ) = = 0xff00 ) {
u8 B = ( wep - > iv > > 16 ) & 0xff ;
if ( B > = 3 & & B < klen )
wep - > iv + = 0x0100 ;
}
/* Prepend 24-bit IV to RC4 key and TX frame */
2005-12-31 13:35:20 +03:00
* pos + + = ( wep - > iv > > 16 ) & 0xff ;
* pos + + = ( wep - > iv > > 8 ) & 0xff ;
* pos + + = wep - > iv & 0xff ;
2005-05-13 06:48:20 +04:00
* pos + + = wep - > key_idx < < 6 ;
2005-12-31 13:35:20 +03:00
return 0 ;
}
/* Perform WEP encryption on given skb that has at least 4 bytes of headroom
* for IV and 4 bytes of tailroom for ICV . 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 ) )
*/
2008-10-29 18:35:05 +03:00
static int lib80211_wep_encrypt ( struct sk_buff * skb , int hdr_len , void * priv )
2005-12-31 13:35:20 +03:00
{
2008-10-29 18:35:05 +03:00
struct lib80211_wep_data * wep = priv ;
2006-09-23 04:10:23 +04:00
struct blkcipher_desc desc = { . tfm = wep - > tx_tfm } ;
2005-12-31 13:35:20 +03:00
u32 crc , klen , len ;
u8 * pos , * icv ;
struct scatterlist sg ;
u8 key [ WEP_KEY_LEN + 3 ] ;
2008-10-29 18:35:05 +03:00
/* other checks are in lib80211_wep_build_iv */
2005-12-31 13:35:20 +03:00
if ( skb_tailroom ( skb ) < 4 )
return - 1 ;
2007-02-09 17:24:46 +03:00
2005-12-31 13:35:20 +03:00
/* add the IV to the frame */
2008-10-29 18:35:05 +03:00
if ( lib80211_wep_build_iv ( skb , hdr_len , NULL , 0 , priv ) )
2005-12-31 13:35:20 +03:00
return - 1 ;
2007-02-09 17:24:46 +03:00
2005-12-31 13:35:20 +03:00
/* Copy the IV into the first 3 bytes of the key */
2007-03-28 01:55:52 +04:00
skb_copy_from_linear_data_offset ( skb , hdr_len , key , 3 ) ;
2005-12-31 13:35:20 +03:00
2005-05-13 06:48:20 +04:00
/* Copy rest of the WEP key (the secret part) */
memcpy ( key + 3 , wep - > key , wep - > key_len ) ;
2007-02-09 17:24:46 +03:00
2005-12-31 13:35:20 +03:00
len = skb - > len - hdr_len - 4 ;
pos = skb - > data + hdr_len + 4 ;
klen = 3 + wep - > key_len ;
2005-05-13 06:48:20 +04:00
2005-12-31 13:35:20 +03:00
/* Append little-endian CRC32 over only the data and encrypt it to produce ICV */
2005-05-13 06:48:20 +04:00
crc = ~ crc32_le ( ~ 0 , pos , len ) ;
icv = skb_put ( skb , 4 ) ;
icv [ 0 ] = crc ;
icv [ 1 ] = crc > > 8 ;
icv [ 2 ] = crc > > 16 ;
icv [ 3 ] = crc > > 24 ;
2006-09-23 04:10:23 +04:00
crypto_blkcipher_setkey ( wep - > tx_tfm , key , klen ) ;
2007-10-22 21:44:26 +04:00
sg_init_one ( & sg , pos , len + 4 ) ;
2006-08-22 14:36:13 +04:00
return crypto_blkcipher_encrypt ( & desc , & sg , & sg , len + 4 ) ;
2005-05-13 06:48:20 +04:00
}
/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of
* the frame : IV ( 4 bytes ) , encrypted payload ( including SNAP header ) ,
* ICV ( 4 bytes ) . 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 .
*/
2008-10-29 18:35:05 +03:00
static int lib80211_wep_decrypt ( struct sk_buff * skb , int hdr_len , void * priv )
2005-05-13 06:48:20 +04:00
{
2008-10-29 18:35:05 +03:00
struct lib80211_wep_data * wep = priv ;
2006-09-23 04:10:23 +04:00
struct blkcipher_desc desc = { . tfm = wep - > rx_tfm } ;
2005-05-13 06:48:20 +04:00
u32 crc , klen , plen ;
u8 key [ WEP_KEY_LEN + 3 ] ;
u8 keyidx , * pos , icv [ 4 ] ;
struct scatterlist sg ;
if ( skb - > len < hdr_len + 8 )
return - 1 ;
pos = skb - > data + hdr_len ;
key [ 0 ] = * pos + + ;
key [ 1 ] = * pos + + ;
key [ 2 ] = * pos + + ;
keyidx = * pos + + > > 6 ;
if ( keyidx ! = wep - > key_idx )
return - 1 ;
klen = 3 + wep - > key_len ;
/* Copy rest of the WEP key (the secret part) */
memcpy ( key + 3 , wep - > key , wep - > key_len ) ;
/* Apply RC4 to data and compute CRC32 over decrypted data */
plen = skb - > len - hdr_len - 8 ;
2006-09-23 04:10:23 +04:00
crypto_blkcipher_setkey ( wep - > rx_tfm , key , klen ) ;
2007-10-22 21:44:26 +04:00
sg_init_one ( & sg , pos , plen + 4 ) ;
2006-08-22 14:36:13 +04:00
if ( crypto_blkcipher_decrypt ( & desc , & sg , & sg , plen + 4 ) )
return - 7 ;
2005-05-13 06:48:20 +04:00
crc = ~ crc32_le ( ~ 0 , pos , plen ) ;
icv [ 0 ] = crc ;
icv [ 1 ] = crc > > 8 ;
icv [ 2 ] = crc > > 16 ;
icv [ 3 ] = crc > > 24 ;
if ( memcmp ( icv , pos + plen , 4 ) ! = 0 ) {
/* ICV mismatch - drop frame */
return - 2 ;
}
/* Remove IV and ICV */
memmove ( skb - > data + 4 , skb - > data , hdr_len ) ;
skb_pull ( skb , 4 ) ;
skb_trim ( skb , skb - > len - 4 ) ;
return 0 ;
}
2008-10-29 18:35:05 +03:00
static int lib80211_wep_set_key ( void * key , int len , u8 * seq , void * priv )
2005-05-13 06:48:20 +04:00
{
2008-10-29 18:35:05 +03:00
struct lib80211_wep_data * wep = priv ;
2005-05-13 06:48:20 +04:00
if ( len < 0 | | len > WEP_KEY_LEN )
return - 1 ;
memcpy ( wep - > key , key , len ) ;
wep - > key_len = len ;
return 0 ;
}
2008-10-29 18:35:05 +03:00
static int lib80211_wep_get_key ( void * key , int len , u8 * seq , void * priv )
2005-05-13 06:48:20 +04:00
{
2008-10-29 18:35:05 +03:00
struct lib80211_wep_data * wep = priv ;
2005-05-13 06:48:20 +04:00
if ( len < wep - > key_len )
return - 1 ;
memcpy ( key , wep - > key , wep - > key_len ) ;
return wep - > key_len ;
}
2013-04-11 00:13:23 +04:00
static void lib80211_wep_print_stats ( struct seq_file * m , void * priv )
2005-05-13 06:48:20 +04:00
{
2008-10-29 18:35:05 +03:00
struct lib80211_wep_data * wep = priv ;
2013-04-11 00:13:23 +04:00
seq_printf ( m , " key[%d] alg=WEP len=%d \n " , wep - > key_idx , wep - > key_len ) ;
2005-05-13 06:48:20 +04:00
}
2008-10-29 18:35:05 +03:00
static struct lib80211_crypto_ops lib80211_crypt_wep = {
2005-09-14 02:35:21 +04:00
. name = " WEP " ,
2008-10-29 18:35:05 +03:00
. init = lib80211_wep_init ,
. deinit = lib80211_wep_deinit ,
. encrypt_mpdu = lib80211_wep_encrypt ,
. decrypt_mpdu = lib80211_wep_decrypt ,
2005-09-14 02:35:21 +04:00
. encrypt_msdu = NULL ,
. decrypt_msdu = NULL ,
2008-10-29 18:35:05 +03:00
. set_key = lib80211_wep_set_key ,
. get_key = lib80211_wep_get_key ,
. print_stats = lib80211_wep_print_stats ,
2005-09-21 20:54:53 +04:00
. extra_mpdu_prefix_len = 4 , /* IV */
. extra_mpdu_postfix_len = 4 , /* ICV */
2005-09-14 02:35:21 +04:00
. owner = THIS_MODULE ,
2005-05-13 06:48:20 +04:00
} ;
2008-10-29 18:35:05 +03:00
static int __init lib80211_crypto_wep_init ( void )
2005-05-13 06:48:20 +04:00
{
2008-10-29 18:35:05 +03:00
return lib80211_register_crypto_ops ( & lib80211_crypt_wep ) ;
2005-05-13 06:48:20 +04:00
}
2008-10-29 18:35:05 +03:00
static void __exit lib80211_crypto_wep_exit ( void )
2005-05-13 06:48:20 +04:00
{
2008-10-29 18:35:05 +03:00
lib80211_unregister_crypto_ops ( & lib80211_crypt_wep ) ;
2005-05-13 06:48:20 +04:00
}
2008-10-29 18:35:05 +03:00
module_init ( lib80211_crypto_wep_init ) ;
module_exit ( lib80211_crypto_wep_exit ) ;