2019-06-04 11:11:34 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2005-05-13 06:48:20 +04:00
/*
2008-10-29 18:35:05 +03:00
* lib80211 crypt : host - based CCMP encryption implementation for lib80211
2005-05-13 06:48:20 +04:00
*
2007-03-25 04:15:30 +04:00
* Copyright ( c ) 2003 - 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
*/
2007-08-29 02:50:33 +04:00
# include <linux/kernel.h>
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>
# include <linux/skbuff.h>
# include <linux/netdevice.h>
# include <linux/if_ether.h>
# include <linux/if_arp.h>
# include <asm/string.h>
# include <linux/wireless.h>
2008-10-29 18:35:05 +03:00
# include <linux/ieee80211.h>
2005-05-13 06:48:20 +04:00
# include <linux/crypto.h>
2019-06-17 12:19:01 +03:00
# include <crypto/aead.h>
2005-05-13 06:48:20 +04:00
2008-10-29 18:35:05 +03:00
# include <net/lib80211.h>
2005-05-13 06:48:20 +04:00
MODULE_AUTHOR ( " Jouni Malinen " ) ;
MODULE_DESCRIPTION ( " Host AP crypt: CCMP " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define AES_BLOCK_LEN 16
# define CCMP_HDR_LEN 8
# define CCMP_MIC_LEN 8
# define CCMP_TK_LEN 16
# define CCMP_PN_LEN 6
2008-10-29 18:35:05 +03:00
struct lib80211_ccmp_data {
2005-05-13 06:48:20 +04:00
u8 key [ CCMP_TK_LEN ] ;
int key_set ;
u8 tx_pn [ CCMP_PN_LEN ] ;
u8 rx_pn [ CCMP_PN_LEN ] ;
u32 dot11RSNAStatsCCMPFormatErrors ;
u32 dot11RSNAStatsCCMPReplays ;
u32 dot11RSNAStatsCCMPDecryptErrors ;
int key_idx ;
2019-06-17 12:19:01 +03:00
struct crypto_aead * tfm ;
2005-05-13 06:48:20 +04:00
/* scratch buffers for virt_to_page() (crypto API) */
2019-06-17 12:19:01 +03:00
u8 tx_aad [ 2 * AES_BLOCK_LEN ] ;
u8 rx_aad [ 2 * AES_BLOCK_LEN ] ;
2005-05-13 06:48:20 +04:00
} ;
2008-10-29 18:35:05 +03:00
static void * lib80211_ccmp_init ( int key_idx )
2005-05-13 06:48:20 +04:00
{
2008-10-29 18:35:05 +03:00
struct lib80211_ccmp_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 = key_idx ;
2019-06-17 12:19:01 +03:00
priv - > tfm = crypto_alloc_aead ( " ccm(aes) " , 0 , CRYPTO_ALG_ASYNC ) ;
2006-08-22 14:36:13 +04:00
if ( IS_ERR ( priv - > tfm ) ) {
priv - > tfm = NULL ;
2005-05-13 06:48:20 +04:00
goto fail ;
}
return priv ;
2005-09-07 08:48:31 +04:00
fail :
2005-05-13 06:48:20 +04:00
if ( priv ) {
if ( priv - > tfm )
2019-06-17 12:19:01 +03:00
crypto_free_aead ( priv - > tfm ) ;
2005-05-13 06:48:20 +04:00
kfree ( priv ) ;
}
return NULL ;
}
2008-10-29 18:35:05 +03:00
static void lib80211_ccmp_deinit ( void * priv )
2005-05-13 06:48:20 +04:00
{
2008-10-29 18:35:05 +03:00
struct lib80211_ccmp_data * _priv = priv ;
2005-05-13 06:48:20 +04:00
if ( _priv & & _priv - > tfm )
2019-06-17 12:19:01 +03:00
crypto_free_aead ( _priv - > tfm ) ;
2005-05-13 06:48:20 +04:00
kfree ( priv ) ;
}
2019-06-17 12:19:01 +03:00
static int ccmp_init_iv_and_aad ( const struct ieee80211_hdr * hdr ,
const u8 * pn , u8 * iv , u8 * aad )
2005-05-13 06:48:20 +04:00
{
u8 * pos , qc = 0 ;
size_t aad_len ;
int a4_included , qc_included ;
2008-10-29 18:35:05 +03:00
a4_included = ieee80211_has_a4 ( hdr - > frame_control ) ;
qc_included = ieee80211_is_data_qos ( hdr - > frame_control ) ;
2005-05-13 06:48:20 +04:00
aad_len = 22 ;
if ( a4_included )
aad_len + = 6 ;
if ( qc_included ) {
2005-09-07 08:48:31 +04:00
pos = ( u8 * ) & hdr - > addr4 ;
2005-05-13 06:48:20 +04:00
if ( a4_included )
pos + = 6 ;
qc = * pos & 0x0f ;
aad_len + = 2 ;
}
2019-06-17 12:19:01 +03:00
/* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
* mode authentication are not allowed to collide , yet both are derived
* from the same vector . We only set L : = 1 here to indicate that the
* data size can be represented in ( L + 1 ) bytes . The CCM layer will take
* care of storing the data length in the top ( L + 1 ) bytes and setting
* and clearing the other bits as is required to derive the two IVs .
*/
iv [ 0 ] = 0x1 ;
/* Nonce: QC | A2 | PN */
iv [ 1 ] = qc ;
memcpy ( iv + 2 , hdr - > addr2 , ETH_ALEN ) ;
memcpy ( iv + 8 , pn , CCMP_PN_LEN ) ;
2005-05-13 06:48:20 +04:00
/* AAD:
* FC with bits 4. .6 and 11. .13 masked to zero ; 14 is always one
* A1 | A2 | A3
* SC with bits 4. .15 ( seq # ) masked to zero
* A4 ( if present )
* QC ( if present )
*/
pos = ( u8 * ) hdr ;
2019-06-17 12:19:01 +03:00
aad [ 0 ] = pos [ 0 ] & 0x8f ;
aad [ 1 ] = pos [ 1 ] & 0xc7 ;
memcpy ( aad + 2 , hdr - > addr1 , 3 * ETH_ALEN ) ;
2008-10-29 18:35:05 +03:00
pos = ( u8 * ) & hdr - > seq_ctrl ;
2019-06-17 12:19:01 +03:00
aad [ 20 ] = pos [ 0 ] & 0x0f ;
aad [ 21 ] = 0 ; /* all bits masked */
memset ( aad + 22 , 0 , 8 ) ;
2005-05-13 06:48:20 +04:00
if ( a4_included )
2019-06-17 12:19:01 +03:00
memcpy ( aad + 22 , hdr - > addr4 , ETH_ALEN ) ;
2005-05-13 06:48:20 +04:00
if ( qc_included ) {
2019-06-17 12:19:01 +03:00
aad [ a4_included ? 28 : 22 ] = qc ;
2005-05-13 06:48:20 +04:00
/* rest of QC masked */
}
2019-06-17 12:19:01 +03:00
return aad_len ;
2005-05-13 06:48:20 +04:00
}
2008-10-29 18:35:05 +03:00
static int lib80211_ccmp_hdr ( struct sk_buff * skb , int hdr_len ,
2006-01-19 11:22:32 +03:00
u8 * aeskey , int keylen , void * priv )
2005-05-13 06:48:20 +04:00
{
2008-10-29 18:35:05 +03:00
struct lib80211_ccmp_data * key = priv ;
2005-09-21 20:58:49 +04:00
int i ;
u8 * pos ;
2005-05-13 06:48:20 +04:00
2005-09-21 20:58:49 +04:00
if ( skb_headroom ( skb ) < CCMP_HDR_LEN | | skb - > len < hdr_len )
2005-05-13 06:48:20 +04:00
return - 1 ;
2006-01-19 11:22:32 +03:00
if ( aeskey ! = NULL & & keylen > = CCMP_TK_LEN )
memcpy ( aeskey , key - > key , CCMP_TK_LEN ) ;
2005-05-13 06:48:20 +04:00
pos = skb_push ( skb , CCMP_HDR_LEN ) ;
memmove ( pos , pos + CCMP_HDR_LEN , hdr_len ) ;
pos + = hdr_len ;
i = CCMP_PN_LEN - 1 ;
while ( i > = 0 ) {
key - > tx_pn [ i ] + + ;
if ( key - > tx_pn [ i ] ! = 0 )
break ;
i - - ;
}
* pos + + = key - > tx_pn [ 5 ] ;
* pos + + = key - > tx_pn [ 4 ] ;
* pos + + = 0 ;
2005-09-07 08:48:31 +04:00
* pos + + = ( key - > key_idx < < 6 ) | ( 1 < < 5 ) /* Ext IV included */ ;
2005-05-13 06:48:20 +04:00
* pos + + = key - > tx_pn [ 3 ] ;
* pos + + = key - > tx_pn [ 2 ] ;
* pos + + = key - > tx_pn [ 1 ] ;
* pos + + = key - > tx_pn [ 0 ] ;
2005-09-21 20:58:49 +04:00
return CCMP_HDR_LEN ;
}
2008-10-29 18:35:05 +03:00
static int lib80211_ccmp_encrypt ( struct sk_buff * skb , int hdr_len , void * priv )
2005-09-21 20:58:49 +04:00
{
2008-10-29 18:35:05 +03:00
struct lib80211_ccmp_data * key = priv ;
struct ieee80211_hdr * hdr ;
2019-06-17 12:19:01 +03:00
struct aead_request * req ;
struct scatterlist sg [ 2 ] ;
u8 * aad = key - > tx_aad ;
u8 iv [ AES_BLOCK_LEN ] ;
int len , data_len , aad_len ;
int ret ;
2005-09-21 20:58:49 +04:00
if ( skb_tailroom ( skb ) < CCMP_MIC_LEN | | skb - > len < hdr_len )
return - 1 ;
data_len = skb - > len - hdr_len ;
2008-10-29 18:35:05 +03:00
len = lib80211_ccmp_hdr ( skb , hdr_len , NULL , 0 , priv ) ;
2005-09-21 20:58:49 +04:00
if ( len < 0 )
return - 1 ;
2019-06-17 12:19:01 +03:00
req = aead_request_alloc ( key - > tfm , GFP_ATOMIC ) ;
if ( ! req )
return - ENOMEM ;
2008-10-29 18:35:05 +03:00
hdr = ( struct ieee80211_hdr * ) skb - > data ;
2019-06-17 12:19:01 +03:00
aad_len = ccmp_init_iv_and_aad ( hdr , key - > tx_pn , iv , aad ) ;
2005-05-13 06:48:20 +04:00
2019-06-17 12:19:01 +03:00
skb_put ( skb , CCMP_MIC_LEN ) ;
2005-05-13 06:48:20 +04:00
2019-06-17 12:19:01 +03:00
sg_init_table ( sg , 2 ) ;
sg_set_buf ( & sg [ 0 ] , aad , aad_len ) ;
sg_set_buf ( & sg [ 1 ] , skb - > data + hdr_len + CCMP_HDR_LEN ,
data_len + CCMP_MIC_LEN ) ;
aead_request_set_callback ( req , 0 , NULL , NULL ) ;
aead_request_set_ad ( req , aad_len ) ;
aead_request_set_crypt ( req , sg , sg , data_len , iv ) ;
ret = crypto_aead_encrypt ( req ) ;
aead_request_free ( req ) ;
return ret ;
2005-05-13 06:48:20 +04:00
}
2006-08-21 07:33:09 +04:00
/*
* deal with seq counter wrapping correctly .
* refer to timer_after ( ) for jiffies wrapping handling
*/
static inline int ccmp_replay_check ( u8 * pn_n , u8 * pn_o )
{
u32 iv32_n , iv16_n ;
u32 iv32_o , iv16_o ;
iv32_n = ( pn_n [ 0 ] < < 24 ) | ( pn_n [ 1 ] < < 16 ) | ( pn_n [ 2 ] < < 8 ) | pn_n [ 3 ] ;
iv16_n = ( pn_n [ 4 ] < < 8 ) | pn_n [ 5 ] ;
iv32_o = ( pn_o [ 0 ] < < 24 ) | ( pn_o [ 1 ] < < 16 ) | ( pn_o [ 2 ] < < 8 ) | pn_o [ 3 ] ;
iv16_o = ( pn_o [ 4 ] < < 8 ) | pn_o [ 5 ] ;
if ( ( s32 ) iv32_n - ( s32 ) iv32_o < 0 | |
( iv32_n = = iv32_o & & iv16_n < = iv16_o ) )
return 1 ;
return 0 ;
}
2008-10-29 18:35:05 +03:00
static int lib80211_ccmp_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_ccmp_data * key = priv ;
2005-05-13 06:48:20 +04:00
u8 keyidx , * pos ;
2008-10-29 18:35:05 +03:00
struct ieee80211_hdr * hdr ;
2019-06-17 12:19:01 +03:00
struct aead_request * req ;
struct scatterlist sg [ 2 ] ;
u8 * aad = key - > rx_aad ;
u8 iv [ AES_BLOCK_LEN ] ;
2005-05-13 06:48:20 +04:00
u8 pn [ 6 ] ;
2019-06-17 12:19:01 +03:00
int aad_len , ret ;
size_t data_len = skb - > len - hdr_len - CCMP_HDR_LEN ;
2005-05-13 06:48:20 +04:00
if ( skb - > len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN ) {
key - > dot11RSNAStatsCCMPFormatErrors + + ;
return - 1 ;
}
2008-10-29 18:35:05 +03:00
hdr = ( struct ieee80211_hdr * ) skb - > data ;
2005-05-13 06:48:20 +04:00
pos = skb - > data + hdr_len ;
keyidx = pos [ 3 ] ;
if ( ! ( keyidx & ( 1 < < 5 ) ) ) {
2012-05-14 01:56:26 +04:00
net_dbg_ratelimited ( " CCMP: received packet without ExtIV flag from %pM \n " ,
hdr - > addr2 ) ;
2005-05-13 06:48:20 +04:00
key - > dot11RSNAStatsCCMPFormatErrors + + ;
return - 2 ;
}
keyidx > > = 6 ;
if ( key - > key_idx ! = keyidx ) {
2015-11-06 14:02:31 +03:00
net_dbg_ratelimited ( " CCMP: RX tkey->key_idx=%d frame keyidx=%d \n " ,
key - > key_idx , keyidx ) ;
2005-05-13 06:48:20 +04:00
return - 6 ;
}
if ( ! key - > key_set ) {
2012-05-14 01:56:26 +04:00
net_dbg_ratelimited ( " CCMP: received packet from %pM with keyid=%d that does not have a configured key \n " ,
hdr - > addr2 , keyidx ) ;
2005-05-13 06:48:20 +04:00
return - 3 ;
}
pn [ 0 ] = pos [ 7 ] ;
pn [ 1 ] = pos [ 6 ] ;
pn [ 2 ] = pos [ 5 ] ;
pn [ 3 ] = pos [ 4 ] ;
pn [ 4 ] = pos [ 1 ] ;
pn [ 5 ] = pos [ 0 ] ;
pos + = 8 ;
2006-08-21 07:33:09 +04:00
if ( ccmp_replay_check ( pn , key - > rx_pn ) ) {
2009-03-11 18:05:25 +03:00
# ifdef CONFIG_LIB80211_DEBUG
2012-05-14 01:56:26 +04:00
net_dbg_ratelimited ( " CCMP: replay detected: STA=%pM previous PN %02x%02x%02x%02x%02x%02x received PN %02x%02x%02x%02x%02x%02x \n " ,
hdr - > addr2 ,
key - > rx_pn [ 0 ] , key - > rx_pn [ 1 ] , key - > rx_pn [ 2 ] ,
key - > rx_pn [ 3 ] , key - > rx_pn [ 4 ] , key - > rx_pn [ 5 ] ,
pn [ 0 ] , pn [ 1 ] , pn [ 2 ] , pn [ 3 ] , pn [ 4 ] , pn [ 5 ] ) ;
2009-03-11 18:05:25 +03:00
# endif
2005-05-13 06:48:20 +04:00
key - > dot11RSNAStatsCCMPReplays + + ;
return - 4 ;
}
2019-06-17 12:19:01 +03:00
req = aead_request_alloc ( key - > tfm , GFP_ATOMIC ) ;
if ( ! req )
return - ENOMEM ;
2005-05-13 06:48:20 +04:00
2019-06-17 12:19:01 +03:00
aad_len = ccmp_init_iv_and_aad ( hdr , pn , iv , aad ) ;
sg_init_table ( sg , 2 ) ;
sg_set_buf ( & sg [ 0 ] , aad , aad_len ) ;
sg_set_buf ( & sg [ 1 ] , pos , data_len ) ;
aead_request_set_callback ( req , 0 , NULL , NULL ) ;
aead_request_set_ad ( req , aad_len ) ;
aead_request_set_crypt ( req , sg , sg , data_len , iv ) ;
ret = crypto_aead_decrypt ( req ) ;
aead_request_free ( req ) ;
if ( ret ) {
net_dbg_ratelimited ( " CCMP: decrypt failed: STA=%pM (%d) \n " ,
hdr - > addr2 , ret ) ;
2005-05-13 06:48:20 +04:00
key - > dot11RSNAStatsCCMPDecryptErrors + + ;
return - 5 ;
}
memcpy ( key - > rx_pn , pn , CCMP_PN_LEN ) ;
/* Remove hdr and MIC */
memmove ( skb - > data + CCMP_HDR_LEN , skb - > data , hdr_len ) ;
skb_pull ( skb , CCMP_HDR_LEN ) ;
skb_trim ( skb , skb - > len - CCMP_MIC_LEN ) ;
return keyidx ;
}
2008-10-29 18:35:05 +03:00
static int lib80211_ccmp_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_ccmp_data * data = priv ;
2005-05-13 06:48:20 +04:00
int keyidx ;
2019-06-17 12:19:01 +03:00
struct crypto_aead * tfm = data - > tfm ;
2005-05-13 06:48:20 +04:00
keyidx = data - > key_idx ;
memset ( data , 0 , sizeof ( * data ) ) ;
data - > key_idx = keyidx ;
data - > tfm = tfm ;
if ( len = = CCMP_TK_LEN ) {
memcpy ( data - > key , key , CCMP_TK_LEN ) ;
data - > key_set = 1 ;
if ( seq ) {
data - > rx_pn [ 0 ] = seq [ 5 ] ;
data - > rx_pn [ 1 ] = seq [ 4 ] ;
data - > rx_pn [ 2 ] = seq [ 3 ] ;
data - > rx_pn [ 3 ] = seq [ 2 ] ;
data - > rx_pn [ 4 ] = seq [ 1 ] ;
data - > rx_pn [ 5 ] = seq [ 0 ] ;
}
2019-06-17 12:19:01 +03:00
if ( crypto_aead_setauthsize ( data - > tfm , CCMP_MIC_LEN ) | |
crypto_aead_setkey ( data - > tfm , data - > key , CCMP_TK_LEN ) )
return - 1 ;
2005-05-13 06:48:20 +04:00
} else if ( len = = 0 )
data - > key_set = 0 ;
else
return - 1 ;
return 0 ;
}
2008-10-29 18:35:05 +03:00
static int lib80211_ccmp_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_ccmp_data * data = priv ;
2005-05-13 06:48:20 +04:00
if ( len < CCMP_TK_LEN )
return - 1 ;
if ( ! data - > key_set )
return 0 ;
memcpy ( key , data - > key , CCMP_TK_LEN ) ;
if ( seq ) {
seq [ 0 ] = data - > tx_pn [ 5 ] ;
seq [ 1 ] = data - > tx_pn [ 4 ] ;
seq [ 2 ] = data - > tx_pn [ 3 ] ;
seq [ 3 ] = data - > tx_pn [ 2 ] ;
seq [ 4 ] = data - > tx_pn [ 1 ] ;
seq [ 5 ] = data - > tx_pn [ 0 ] ;
}
return CCMP_TK_LEN ;
}
2013-04-11 00:13:23 +04:00
static void lib80211_ccmp_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_ccmp_data * ccmp = priv ;
2007-10-04 04:59:30 +04:00
2013-04-11 00:13:23 +04:00
seq_printf ( m ,
" key[%d] alg=CCMP key_set=%d "
" tx_pn=%02x%02x%02x%02x%02x%02x "
" rx_pn=%02x%02x%02x%02x%02x%02x "
" format_errors=%d replays=%d decrypt_errors=%d \n " ,
ccmp - > key_idx , ccmp - > key_set ,
ccmp - > tx_pn [ 0 ] , ccmp - > tx_pn [ 1 ] , ccmp - > tx_pn [ 2 ] ,
ccmp - > tx_pn [ 3 ] , ccmp - > tx_pn [ 4 ] , ccmp - > tx_pn [ 5 ] ,
ccmp - > rx_pn [ 0 ] , ccmp - > rx_pn [ 1 ] , ccmp - > rx_pn [ 2 ] ,
ccmp - > rx_pn [ 3 ] , ccmp - > rx_pn [ 4 ] , ccmp - > rx_pn [ 5 ] ,
ccmp - > dot11RSNAStatsCCMPFormatErrors ,
ccmp - > dot11RSNAStatsCCMPReplays ,
ccmp - > dot11RSNAStatsCCMPDecryptErrors ) ;
2005-05-13 06:48:20 +04:00
}
2008-10-29 18:35:05 +03:00
static struct lib80211_crypto_ops lib80211_crypt_ccmp = {
2005-09-14 02:35:21 +04:00
. name = " CCMP " ,
2008-10-29 18:35:05 +03:00
. init = lib80211_ccmp_init ,
. deinit = lib80211_ccmp_deinit ,
. encrypt_mpdu = lib80211_ccmp_encrypt ,
. decrypt_mpdu = lib80211_ccmp_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_ccmp_set_key ,
. get_key = lib80211_ccmp_get_key ,
. print_stats = lib80211_ccmp_print_stats ,
2005-09-21 20:54:53 +04:00
. extra_mpdu_prefix_len = CCMP_HDR_LEN ,
. extra_mpdu_postfix_len = CCMP_MIC_LEN ,
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_ccmp_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_ccmp ) ;
2005-05-13 06:48:20 +04:00
}
2008-10-29 18:35:05 +03:00
static void __exit lib80211_crypto_ccmp_exit ( void )
2005-05-13 06:48:20 +04:00
{
2008-10-29 18:35:05 +03:00
lib80211_unregister_crypto_ops ( & lib80211_crypt_ccmp ) ;
2005-05-13 06:48:20 +04:00
}
2008-10-29 18:35:05 +03:00
module_init ( lib80211_crypto_ccmp_init ) ;
module_exit ( lib80211_crypto_ccmp_exit ) ;