2007-05-05 22:45:53 +04:00
/*
* Michael MIC implementation - optimized for TKIP MIC operations
* Copyright 2002 - 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/types.h>
2008-05-15 03:26:16 +04:00
# include <linux/bitops.h>
2008-07-02 22:05:35 +04:00
# include <linux/ieee80211.h>
2008-05-15 03:26:16 +04:00
# include <asm/unaligned.h>
2007-05-05 22:45:53 +04:00
# include "michael.h"
2008-05-15 03:26:17 +04:00
static void michael_block ( struct michael_mic_ctx * mctx , u32 val )
{
mctx - > l ^ = val ;
mctx - > r ^ = rol32 ( mctx - > l , 17 ) ;
mctx - > l + = mctx - > r ;
mctx - > r ^ = ( ( mctx - > l & 0xff00ff00 ) > > 8 ) |
( ( mctx - > l & 0x00ff00ff ) < < 8 ) ;
mctx - > l + = mctx - > r ;
mctx - > r ^ = rol32 ( mctx - > l , 3 ) ;
mctx - > l + = mctx - > r ;
mctx - > r ^ = ror32 ( mctx - > l , 2 ) ;
mctx - > l + = mctx - > r ;
}
2008-07-02 22:05:35 +04:00
static void michael_mic_hdr ( struct michael_mic_ctx * mctx , const u8 * key ,
struct ieee80211_hdr * hdr )
2008-05-15 03:26:17 +04:00
{
2008-07-02 22:05:35 +04:00
u8 * da , * sa , tid ;
da = ieee80211_get_DA ( hdr ) ;
sa = ieee80211_get_SA ( hdr ) ;
if ( ieee80211_is_data_qos ( hdr - > frame_control ) )
tid = * ieee80211_get_qos_ctl ( hdr ) & IEEE80211_QOS_CTL_TID_MASK ;
else
tid = 0 ;
2008-05-15 03:26:17 +04:00
mctx - > l = get_unaligned_le32 ( key ) ;
mctx - > r = get_unaligned_le32 ( key + 4 ) ;
/*
* A pseudo header ( DA , SA , Priority , 0 , 0 , 0 ) is used in Michael MIC
* calculation , but it is _not_ transmitted
*/
michael_block ( mctx , get_unaligned_le32 ( da ) ) ;
michael_block ( mctx , get_unaligned_le16 ( & da [ 4 ] ) |
( get_unaligned_le16 ( sa ) < < 16 ) ) ;
michael_block ( mctx , get_unaligned_le32 ( & sa [ 2 ] ) ) ;
2008-07-02 22:05:35 +04:00
michael_block ( mctx , tid ) ;
2008-05-15 03:26:17 +04:00
}
2007-05-05 22:45:53 +04:00
2008-07-02 22:05:35 +04:00
void michael_mic ( const u8 * key , struct ieee80211_hdr * hdr ,
2008-05-15 03:26:18 +04:00
const u8 * data , size_t data_len , u8 * mic )
2007-05-05 22:45:53 +04:00
{
2008-05-15 03:26:17 +04:00
u32 val ;
2007-05-05 22:45:53 +04:00
size_t block , blocks , left ;
2008-05-15 03:26:17 +04:00
struct michael_mic_ctx mctx ;
2007-05-05 22:45:53 +04:00
2008-07-02 22:05:35 +04:00
michael_mic_hdr ( & mctx , key , hdr ) ;
2007-05-05 22:45:53 +04:00
/* Real data */
blocks = data_len / 4 ;
left = data_len % 4 ;
2008-05-15 03:26:17 +04:00
for ( block = 0 ; block < blocks ; block + + )
michael_block ( & mctx , get_unaligned_le32 ( & data [ block * 4 ] ) ) ;
2007-05-05 22:45:53 +04:00
/* Partial block of 0..3 bytes and padding: 0x5a + 4..7 zeros to make
* total length a multiple of 4. */
val = 0x5a ;
while ( left > 0 ) {
val < < = 8 ;
left - - ;
val | = data [ blocks * 4 + left ] ;
}
2008-05-15 03:26:17 +04:00
michael_block ( & mctx , val ) ;
michael_block ( & mctx , 0 ) ;
put_unaligned_le32 ( mctx . l , mic ) ;
put_unaligned_le32 ( mctx . r , mic + 4 ) ;
2007-05-05 22:45:53 +04:00
}