2006-10-28 07:15:24 +04:00
/*
* Copyright ( C ) 2006 USAGI / WIDE Project
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
* Author :
* Kazunori Miyazawa < miyazawa @ linux - ipv6 . org >
*/
2009-07-12 08:48:32 +04:00
# include <crypto/internal/hash.h>
2006-10-28 07:15:24 +04:00
# include <linux/err.h>
# include <linux/kernel.h>
2011-05-27 22:41:48 +04:00
# include <linux/module.h>
2006-10-28 07:15:24 +04:00
2006-11-17 05:43:04 +03:00
static u_int32_t ks [ 12 ] = { 0x01010101 , 0x01010101 , 0x01010101 , 0x01010101 ,
0x02020202 , 0x02020202 , 0x02020202 , 0x02020202 ,
0x03030303 , 0x03030303 , 0x03030303 , 0x03030303 } ;
2009-07-22 10:37:15 +04:00
2006-10-28 07:15:24 +04:00
/*
* + - - - - - - - - - - - - - - - - - - - - - - - -
* | < parent tfm >
* + - - - - - - - - - - - - - - - - - - - - - - - -
2009-07-22 10:37:15 +04:00
* | xcbc_tfm_ctx
2006-10-28 07:15:24 +04:00
* + - - - - - - - - - - - - - - - - - - - - - - - -
2009-07-22 10:37:15 +04:00
* | consts ( block size * 2 )
2006-10-28 07:15:24 +04:00
* + - - - - - - - - - - - - - - - - - - - - - - - -
2009-07-22 10:37:15 +04:00
*/
struct xcbc_tfm_ctx {
struct crypto_cipher * child ;
u8 ctx [ ] ;
} ;
/*
2006-10-28 07:15:24 +04:00
* + - - - - - - - - - - - - - - - - - - - - - - - -
2009-07-22 10:37:15 +04:00
* | < shash desc >
2006-10-28 07:15:24 +04:00
* + - - - - - - - - - - - - - - - - - - - - - - - -
2009-07-22 10:37:15 +04:00
* | xcbc_desc_ctx
* + - - - - - - - - - - - - - - - - - - - - - - - -
* | odds ( block size )
* + - - - - - - - - - - - - - - - - - - - - - - - -
* | prev ( block size )
2006-10-28 07:15:24 +04:00
* + - - - - - - - - - - - - - - - - - - - - - - - -
*/
2009-07-22 10:37:15 +04:00
struct xcbc_desc_ctx {
2006-10-28 07:15:24 +04:00
unsigned int len ;
2009-07-22 10:37:15 +04:00
u8 ctx [ ] ;
2006-10-28 07:15:24 +04:00
} ;
2009-07-22 10:37:15 +04:00
static int crypto_xcbc_digest_setkey ( struct crypto_shash * parent ,
const u8 * inkey , unsigned int keylen )
2006-10-28 07:15:24 +04:00
{
2009-07-22 10:37:15 +04:00
unsigned long alignmask = crypto_shash_alignmask ( parent ) ;
struct xcbc_tfm_ctx * ctx = crypto_shash_ctx ( parent ) ;
2009-07-12 08:48:32 +04:00
int bs = crypto_shash_blocksize ( parent ) ;
2009-07-22 10:37:15 +04:00
u8 * consts = PTR_ALIGN ( & ctx - > ctx [ 0 ] , alignmask + 1 ) ;
2006-10-28 07:15:24 +04:00
int err = 0 ;
u8 key1 [ bs ] ;
2009-07-22 10:37:15 +04:00
if ( ( err = crypto_cipher_setkey ( ctx - > child , inkey , keylen ) ) )
return err ;
2006-10-28 07:15:24 +04:00
2009-07-22 10:37:15 +04:00
crypto_cipher_encrypt_one ( ctx - > child , consts , ( u8 * ) ks + bs ) ;
crypto_cipher_encrypt_one ( ctx - > child , consts + bs , ( u8 * ) ks + bs * 2 ) ;
crypto_cipher_encrypt_one ( ctx - > child , key1 , ( u8 * ) ks ) ;
2006-10-28 07:15:24 +04:00
return crypto_cipher_setkey ( ctx - > child , key1 , bs ) ;
}
2009-07-12 08:48:32 +04:00
static int crypto_xcbc_digest_init ( struct shash_desc * pdesc )
2006-10-28 07:15:24 +04:00
{
2009-07-22 10:37:15 +04:00
unsigned long alignmask = crypto_shash_alignmask ( pdesc - > tfm ) ;
struct xcbc_desc_ctx * ctx = shash_desc_ctx ( pdesc ) ;
2009-07-12 08:48:32 +04:00
int bs = crypto_shash_blocksize ( pdesc - > tfm ) ;
2009-07-22 10:37:15 +04:00
u8 * prev = PTR_ALIGN ( & ctx - > ctx [ 0 ] , alignmask + 1 ) + bs ;
2006-10-28 07:15:24 +04:00
ctx - > len = 0 ;
2009-07-22 10:37:15 +04:00
memset ( prev , 0 , bs ) ;
2006-10-28 07:15:24 +04:00
return 0 ;
}
2009-07-12 08:48:32 +04:00
static int crypto_xcbc_digest_update ( struct shash_desc * pdesc , const u8 * p ,
unsigned int len )
2006-10-28 07:15:24 +04:00
{
2009-07-12 08:48:32 +04:00
struct crypto_shash * parent = pdesc - > tfm ;
2009-07-22 10:37:15 +04:00
unsigned long alignmask = crypto_shash_alignmask ( parent ) ;
struct xcbc_tfm_ctx * tctx = crypto_shash_ctx ( parent ) ;
struct xcbc_desc_ctx * ctx = shash_desc_ctx ( pdesc ) ;
struct crypto_cipher * tfm = tctx - > child ;
2009-07-12 08:48:32 +04:00
int bs = crypto_shash_blocksize ( parent ) ;
2009-07-22 10:37:15 +04:00
u8 * odds = PTR_ALIGN ( & ctx - > ctx [ 0 ] , alignmask + 1 ) ;
u8 * prev = odds + bs ;
2009-07-12 08:48:32 +04:00
/* checking the data can fill the block */
if ( ( ctx - > len + len ) < = bs ) {
2009-07-22 10:37:15 +04:00
memcpy ( odds + ctx - > len , p , len ) ;
2009-07-12 08:48:32 +04:00
ctx - > len + = len ;
return 0 ;
2008-04-02 10:36:09 +04:00
}
2006-10-28 07:15:24 +04:00
2009-07-12 08:48:32 +04:00
/* filling odds with new data and encrypting it */
2009-07-22 10:37:15 +04:00
memcpy ( odds + ctx - > len , p , bs - ctx - > len ) ;
2009-07-12 08:48:32 +04:00
len - = bs - ctx - > len ;
p + = bs - ctx - > len ;
2006-10-28 07:15:24 +04:00
2009-07-22 10:37:15 +04:00
crypto_xor ( prev , odds , bs ) ;
crypto_cipher_encrypt_one ( tfm , prev , prev ) ;
2009-07-12 08:48:32 +04:00
/* clearing the length */
ctx - > len = 0 ;
/* encrypting the rest of data */
while ( len > bs ) {
2009-07-22 10:37:15 +04:00
crypto_xor ( prev , p , bs ) ;
crypto_cipher_encrypt_one ( tfm , prev , prev ) ;
2009-07-12 08:48:32 +04:00
p + = bs ;
len - = bs ;
}
/* keeping the surplus of blocksize */
if ( len ) {
2009-07-22 10:37:15 +04:00
memcpy ( odds , p , len ) ;
2009-07-12 08:48:32 +04:00
ctx - > len = len ;
}
return 0 ;
2006-12-10 02:45:28 +03:00
}
2009-07-12 08:48:32 +04:00
static int crypto_xcbc_digest_final ( struct shash_desc * pdesc , u8 * out )
2006-10-28 07:15:24 +04:00
{
2009-07-12 08:48:32 +04:00
struct crypto_shash * parent = pdesc - > tfm ;
2009-07-22 10:37:15 +04:00
unsigned long alignmask = crypto_shash_alignmask ( parent ) ;
struct xcbc_tfm_ctx * tctx = crypto_shash_ctx ( parent ) ;
struct xcbc_desc_ctx * ctx = shash_desc_ctx ( pdesc ) ;
struct crypto_cipher * tfm = tctx - > child ;
2009-07-12 08:48:32 +04:00
int bs = crypto_shash_blocksize ( parent ) ;
2009-07-22 10:37:15 +04:00
u8 * consts = PTR_ALIGN ( & tctx - > ctx [ 0 ] , alignmask + 1 ) ;
u8 * odds = PTR_ALIGN ( & ctx - > ctx [ 0 ] , alignmask + 1 ) ;
u8 * prev = odds + bs ;
unsigned int offset = 0 ;
2006-10-28 07:15:24 +04:00
2009-07-22 10:37:15 +04:00
if ( ctx - > len ! = bs ) {
2006-10-28 07:15:24 +04:00
unsigned int rlen ;
2009-07-22 10:37:15 +04:00
u8 * p = odds + ctx - > len ;
2006-10-28 07:15:24 +04:00
* p = 0x80 ;
p + + ;
rlen = bs - ctx - > len - 1 ;
if ( rlen )
memset ( p , 0 , rlen ) ;
2009-07-22 10:37:15 +04:00
offset + = bs ;
}
2006-10-28 07:15:24 +04:00
2009-07-22 10:37:15 +04:00
crypto_xor ( prev , odds , bs ) ;
crypto_xor ( prev , consts + offset , bs ) ;
2006-10-28 07:15:24 +04:00
2009-07-22 10:37:15 +04:00
crypto_cipher_encrypt_one ( tfm , out , prev ) ;
2006-10-28 07:15:24 +04:00
return 0 ;
}
static int xcbc_init_tfm ( struct crypto_tfm * tfm )
{
2006-12-17 02:05:58 +03:00
struct crypto_cipher * cipher ;
2006-10-28 07:15:24 +04:00
struct crypto_instance * inst = ( void * ) tfm - > __crt_alg ;
struct crypto_spawn * spawn = crypto_instance_ctx ( inst ) ;
2009-07-22 10:37:15 +04:00
struct xcbc_tfm_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
2006-10-28 07:15:24 +04:00
2006-12-17 02:05:58 +03:00
cipher = crypto_spawn_cipher ( spawn ) ;
if ( IS_ERR ( cipher ) )
return PTR_ERR ( cipher ) ;
2006-10-28 07:15:24 +04:00
2006-12-17 02:05:58 +03:00
ctx - > child = cipher ;
2006-10-28 07:15:24 +04:00
return 0 ;
} ;
static void xcbc_exit_tfm ( struct crypto_tfm * tfm )
{
2009-07-22 10:37:15 +04:00
struct xcbc_tfm_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
2006-10-28 07:15:24 +04:00
crypto_free_cipher ( ctx - > child ) ;
}
2009-07-12 08:48:32 +04:00
static int xcbc_create ( struct crypto_template * tmpl , struct rtattr * * tb )
2006-10-28 07:15:24 +04:00
{
2009-07-12 08:48:32 +04:00
struct shash_instance * inst ;
2006-10-28 07:15:24 +04:00
struct crypto_alg * alg ;
2009-08-20 11:58:04 +04:00
unsigned long alignmask ;
2007-01-01 10:37:02 +03:00
int err ;
2009-07-12 08:48:32 +04:00
err = crypto_check_attr_type ( tb , CRYPTO_ALG_TYPE_SHASH ) ;
2007-01-01 10:37:02 +03:00
if ( err )
2009-07-12 08:48:32 +04:00
return err ;
2007-01-01 10:37:02 +03:00
alg = crypto_get_attr_alg ( tb , CRYPTO_ALG_TYPE_CIPHER ,
CRYPTO_ALG_TYPE_MASK ) ;
2006-10-28 07:15:24 +04:00
if ( IS_ERR ( alg ) )
2009-07-12 08:48:32 +04:00
return PTR_ERR ( alg ) ;
2006-10-28 07:15:24 +04:00
switch ( alg - > cra_blocksize ) {
case 16 :
break ;
default :
2008-01-01 07:44:50 +03:00
goto out_put_alg ;
2006-10-28 07:15:24 +04:00
}
2009-07-12 08:48:32 +04:00
inst = shash_alloc_instance ( " xcbc " , alg ) ;
2009-07-15 12:53:33 +04:00
err = PTR_ERR ( inst ) ;
2006-10-28 07:15:24 +04:00
if ( IS_ERR ( inst ) )
goto out_put_alg ;
2009-07-12 08:48:32 +04:00
err = crypto_init_spawn ( shash_instance_ctx ( inst ) , alg ,
shash_crypto_instance ( inst ) ,
CRYPTO_ALG_TYPE_MASK ) ;
if ( err )
goto out_free_inst ;
2009-08-20 11:58:04 +04:00
alignmask = alg - > cra_alignmask | 3 ;
inst - > alg . base . cra_alignmask = alignmask ;
2009-07-12 08:48:32 +04:00
inst - > alg . base . cra_priority = alg - > cra_priority ;
inst - > alg . base . cra_blocksize = alg - > cra_blocksize ;
inst - > alg . digestsize = alg - > cra_blocksize ;
2009-07-22 10:37:15 +04:00
inst - > alg . descsize = ALIGN ( sizeof ( struct xcbc_desc_ctx ) ,
crypto_tfm_ctx_alignment ( ) ) +
2009-08-20 11:58:04 +04:00
( alignmask &
2009-07-22 10:37:15 +04:00
~ ( crypto_tfm_ctx_alignment ( ) - 1 ) ) +
alg - > cra_blocksize * 2 ;
inst - > alg . base . cra_ctxsize = ALIGN ( sizeof ( struct xcbc_tfm_ctx ) ,
2009-08-20 11:58:04 +04:00
alignmask + 1 ) +
2009-07-22 10:37:15 +04:00
alg - > cra_blocksize * 2 ;
2009-07-12 08:48:32 +04:00
inst - > alg . base . cra_init = xcbc_init_tfm ;
inst - > alg . base . cra_exit = xcbc_exit_tfm ;
inst - > alg . init = crypto_xcbc_digest_init ;
inst - > alg . update = crypto_xcbc_digest_update ;
inst - > alg . final = crypto_xcbc_digest_final ;
inst - > alg . setkey = crypto_xcbc_digest_setkey ;
err = shash_register_instance ( tmpl , inst ) ;
if ( err ) {
out_free_inst :
shash_free_instance ( shash_crypto_instance ( inst ) ) ;
}
2006-10-28 07:15:24 +04:00
out_put_alg :
crypto_mod_put ( alg ) ;
2009-07-12 08:48:32 +04:00
return err ;
2006-10-28 07:15:24 +04:00
}
static struct crypto_template crypto_xcbc_tmpl = {
. name = " xcbc " ,
2009-07-12 08:48:32 +04:00
. create = xcbc_create ,
. free = shash_free_instance ,
2006-10-28 07:15:24 +04:00
. module = THIS_MODULE ,
} ;
static int __init crypto_xcbc_module_init ( void )
{
return crypto_register_template ( & crypto_xcbc_tmpl ) ;
}
static void __exit crypto_xcbc_module_exit ( void )
{
crypto_unregister_template ( & crypto_xcbc_tmpl ) ;
}
module_init ( crypto_xcbc_module_init ) ;
module_exit ( crypto_xcbc_module_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " XCBC keyed hash algorithm " ) ;