2019-05-19 16:51:43 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2006-10-28 07:15:24 +04:00
/*
* Copyright ( C ) 2006 USAGI / WIDE Project
*
* Author :
* Kazunori Miyazawa < miyazawa @ linux - ipv6 . org >
*/
2020-12-11 15:27:15 +03:00
# include <crypto/internal/cipher.h>
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
} ;
2018-08-08 00:18:35 +03:00
# define XCBC_BLOCKSIZE 16
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 ) ;
u8 * consts = PTR_ALIGN ( & ctx - > ctx [ 0 ] , alignmask + 1 ) ;
2006-10-28 07:15:24 +04:00
int err = 0 ;
2018-08-08 00:18:35 +03:00
u8 key1 [ XCBC_BLOCKSIZE ] ;
int bs = sizeof ( key1 ) ;
2006-10-28 07:15:24 +04:00
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 ;
2020-01-03 06:59:05 +03:00
struct crypto_cipher_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 ;
2020-01-03 06:59:04 +03:00
struct crypto_cipher_spawn * spawn ;
2006-10-28 07:15:24 +04:00
struct crypto_alg * alg ;
2009-08-20 11:58:04 +04:00
unsigned long alignmask ;
2020-07-10 09:20:38 +03:00
u32 mask ;
2007-01-01 10:37:02 +03:00
int err ;
2020-07-10 09:20:38 +03:00
err = crypto_check_attr_type ( tb , CRYPTO_ALG_TYPE_SHASH , & mask ) ;
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
2020-01-03 06:59:04 +03:00
inst = kzalloc ( sizeof ( * inst ) + sizeof ( * spawn ) , GFP_KERNEL ) ;
if ( ! inst )
return - ENOMEM ;
spawn = shash_instance_ctx ( inst ) ;
2006-10-28 07:15:24 +04:00
2020-01-03 06:59:04 +03:00
err = crypto_grab_cipher ( spawn , shash_crypto_instance ( inst ) ,
2020-07-10 09:20:38 +03:00
crypto_attr_alg_name ( tb [ 1 ] ) , 0 , mask ) ;
2020-01-03 06:59:04 +03:00
if ( err )
goto err_free_inst ;
alg = crypto_spawn_cipher_alg ( spawn ) ;
2006-10-28 07:15:24 +04:00
2020-01-03 06:59:04 +03:00
err = - EINVAL ;
if ( alg - > cra_blocksize ! = XCBC_BLOCKSIZE )
goto err_free_inst ;
2006-10-28 07:15:24 +04:00
2020-01-03 06:59:04 +03:00
err = crypto_inst_setname ( shash_crypto_instance ( inst ) , tmpl - > name , alg ) ;
2009-07-12 08:48:32 +04:00
if ( err )
2020-01-03 06:59:04 +03:00
goto err_free_inst ;
2009-07-12 08:48:32 +04:00
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 ;
2020-01-03 07:04:38 +03:00
inst - > free = shash_free_singlespawn_instance ;
2009-07-12 08:48:32 +04:00
err = shash_register_instance ( tmpl , inst ) ;
if ( err ) {
2020-01-03 06:59:04 +03:00
err_free_inst :
2020-01-03 07:04:38 +03:00
shash_free_singlespawn_instance ( inst ) ;
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 ,
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 ) ;
}
2019-04-12 07:57:42 +03:00
subsys_initcall ( crypto_xcbc_module_init ) ;
2006-10-28 07:15:24 +04:00
module_exit ( crypto_xcbc_module_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " XCBC keyed hash algorithm " ) ;
2014-11-25 03:32:38 +03:00
MODULE_ALIAS_CRYPTO ( " xcbc " ) ;
2020-12-11 15:27:15 +03:00
MODULE_IMPORT_NS ( CRYPTO_INTERNAL ) ;