2005-04-17 02:20:36 +04:00
/*
* Cryptographic API .
*
* HMAC : Keyed - Hashing for Message Authentication ( RFC2104 ) .
*
* Copyright ( c ) 2002 James Morris < jmorris @ intercode . com . au >
2006-08-21 14:50:52 +04:00
* Copyright ( c ) 2006 Herbert Xu < herbert @ gondor . apana . org . au >
2005-04-17 02:20:36 +04:00
*
* The HMAC implementation is derived from USAGI .
* Copyright ( c ) 2002 Kazunori Miyazawa < miyazawa @ linux - ipv6 . org > / USAGI
*
* 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
2006-08-21 14:50:52 +04:00
* Software Foundation ; either version 2 of the License , or ( at your option )
2005-04-17 02:20:36 +04:00
* any later version .
*
*/
2006-08-21 14:50:52 +04:00
2008-08-31 16:21:09 +04:00
# include <crypto/internal/hash.h>
2007-12-05 12:59:25 +03:00
# include <crypto/scatterwalk.h>
2006-08-21 14:50:52 +04:00
# include <linux/err.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
2005-09-17 11:55:31 +04:00
# include <linux/scatterlist.h>
2006-08-21 14:50:52 +04:00
# include <linux/slab.h>
# include <linux/string.h>
struct hmac_ctx {
2009-07-09 08:43:37 +04:00
struct shash_desc * desc ;
2006-08-21 14:50:52 +04:00
} ;
2005-04-17 02:20:36 +04:00
2006-08-21 14:50:52 +04:00
static inline void * align_ptr ( void * p , unsigned int align )
{
return ( void * ) ALIGN ( ( unsigned long ) p , align ) ;
}
2009-07-09 08:43:37 +04:00
static inline struct hmac_ctx * hmac_ctx ( struct crypto_shash * tfm )
2006-08-21 14:50:52 +04:00
{
2009-07-09 08:43:37 +04:00
return align_ptr ( crypto_shash_ctx_aligned ( tfm ) +
crypto_shash_blocksize ( tfm ) * 2 +
crypto_shash_digestsize ( tfm ) ,
crypto_tfm_ctx_alignment ( ) ) ;
2006-08-21 14:50:52 +04:00
}
2009-07-09 08:43:37 +04:00
static int hmac_setkey ( struct crypto_shash * parent ,
2006-08-21 14:50:52 +04:00
const u8 * inkey , unsigned int keylen )
{
2009-07-09 08:43:37 +04:00
int bs = crypto_shash_blocksize ( parent ) ;
int ds = crypto_shash_digestsize ( parent ) ;
char * ipad = crypto_shash_ctx_aligned ( parent ) ;
2006-08-21 14:50:52 +04:00
char * opad = ipad + bs ;
char * digest = opad + bs ;
2009-07-09 08:43:37 +04:00
struct hmac_ctx * ctx = align_ptr ( digest + ds ,
crypto_tfm_ctx_alignment ( ) ) ;
2006-08-21 14:50:52 +04:00
unsigned int i ;
if ( keylen > bs ) {
int err ;
2009-07-09 08:43:37 +04:00
ctx - > desc - > flags = crypto_shash_get_flags ( parent ) &
CRYPTO_TFM_REQ_MAY_SLEEP ;
2006-08-21 14:50:52 +04:00
2009-07-09 08:43:37 +04:00
err = crypto_shash_digest ( ctx - > desc , inkey , keylen , digest ) ;
2006-08-21 14:50:52 +04:00
if ( err )
return err ;
inkey = digest ;
keylen = ds ;
}
memcpy ( ipad , inkey , keylen ) ;
memset ( ipad + keylen , 0 , bs - keylen ) ;
memcpy ( opad , ipad , bs ) ;
for ( i = 0 ; i < bs ; i + + ) {
ipad [ i ] ^ = 0x36 ;
opad [ i ] ^ = 0x5c ;
}
return 0 ;
}
2009-07-09 08:43:37 +04:00
static int hmac_init ( struct shash_desc * pdesc )
2006-08-21 14:50:52 +04:00
{
2009-07-09 08:43:37 +04:00
struct crypto_shash * parent = pdesc - > tfm ;
int bs = crypto_shash_blocksize ( parent ) ;
int ds = crypto_shash_digestsize ( parent ) ;
char * ipad = crypto_shash_ctx_aligned ( parent ) ;
struct hmac_ctx * ctx = align_ptr ( ipad + bs * 2 + ds ,
crypto_tfm_ctx_alignment ( ) ) ;
struct shash_desc * desc = shash_desc_ctx ( pdesc ) ;
desc - > tfm = ctx - > desc - > tfm ;
desc - > flags = pdesc - > flags & CRYPTO_TFM_REQ_MAY_SLEEP ;
return crypto_shash_init ( desc ) ? :
crypto_shash_update ( desc , ipad , bs ) ;
2006-08-21 14:50:52 +04:00
}
2009-07-09 08:43:37 +04:00
static int hmac_update ( struct shash_desc * pdesc ,
const u8 * data , unsigned int nbytes )
2006-08-21 14:50:52 +04:00
{
2009-07-09 08:43:37 +04:00
struct shash_desc * desc = shash_desc_ctx ( pdesc ) ;
2006-08-21 14:50:52 +04:00
2009-07-09 08:43:37 +04:00
desc - > flags = pdesc - > flags & CRYPTO_TFM_REQ_MAY_SLEEP ;
2006-08-21 14:50:52 +04:00
2009-07-09 08:43:37 +04:00
return crypto_shash_update ( desc , data , nbytes ) ;
2006-08-21 14:50:52 +04:00
}
2009-07-09 08:43:37 +04:00
static int hmac_final ( struct shash_desc * pdesc , u8 * out )
2006-08-21 14:50:52 +04:00
{
2009-07-09 08:43:37 +04:00
struct crypto_shash * parent = pdesc - > tfm ;
int bs = crypto_shash_blocksize ( parent ) ;
int ds = crypto_shash_digestsize ( parent ) ;
char * opad = crypto_shash_ctx_aligned ( parent ) + bs ;
2006-08-21 14:50:52 +04:00
char * digest = opad + bs ;
2009-07-09 08:43:37 +04:00
struct shash_desc * desc = shash_desc_ctx ( pdesc ) ;
2006-08-21 14:50:52 +04:00
2009-07-09 08:43:37 +04:00
desc - > flags = pdesc - > flags & CRYPTO_TFM_REQ_MAY_SLEEP ;
2006-09-24 03:30:19 +04:00
2009-07-09 08:43:37 +04:00
return crypto_shash_final ( desc , digest ) ? :
crypto_shash_digest ( desc , opad , bs + ds , out ) ;
2006-08-21 14:50:52 +04:00
}
2009-07-09 08:43:37 +04:00
static int hmac_finup ( struct shash_desc * pdesc , const u8 * data ,
unsigned int nbytes , u8 * out )
2006-08-21 14:50:52 +04:00
{
2009-07-09 08:43:37 +04:00
struct crypto_shash * parent = pdesc - > tfm ;
int bs = crypto_shash_blocksize ( parent ) ;
int ds = crypto_shash_digestsize ( parent ) ;
char * opad = crypto_shash_ctx_aligned ( parent ) + bs ;
char * digest = opad + bs ;
struct shash_desc * desc = shash_desc_ctx ( pdesc ) ;
2007-10-22 21:40:16 +04:00
2009-07-09 08:43:37 +04:00
desc - > flags = pdesc - > flags & CRYPTO_TFM_REQ_MAY_SLEEP ;
2006-08-21 14:50:52 +04:00
2009-07-09 08:43:37 +04:00
return crypto_shash_finup ( desc , data , nbytes , digest ) ? :
crypto_shash_digest ( desc , opad , bs + ds , out ) ;
2006-08-21 14:50:52 +04:00
}
static int hmac_init_tfm ( struct crypto_tfm * tfm )
{
2009-07-09 08:43:37 +04:00
struct crypto_shash * parent = __crypto_shash_cast ( tfm ) ;
struct crypto_shash * hash ;
2006-08-21 14:50:52 +04:00
struct crypto_instance * inst = ( void * ) tfm - > __crt_alg ;
2009-07-09 08:43:37 +04:00
struct crypto_shash_spawn * spawn = crypto_instance_ctx ( inst ) ;
struct hmac_ctx * ctx = hmac_ctx ( parent ) ;
2006-08-21 14:50:52 +04:00
2009-07-09 08:43:37 +04:00
hash = crypto_spawn_shash ( spawn ) ;
2006-12-17 02:05:58 +03:00
if ( IS_ERR ( hash ) )
return PTR_ERR ( hash ) ;
2006-08-21 14:50:52 +04:00
2009-07-09 08:43:37 +04:00
parent - > descsize = sizeof ( struct shash_desc ) +
crypto_shash_descsize ( hash ) ;
ctx - > desc = kmalloc ( parent - > descsize , GFP_KERNEL ) ;
if ( ! ctx - > desc ) {
crypto_free_shash ( hash ) ;
return - ENOMEM ;
}
ctx - > desc - > tfm = hash ;
2006-08-21 14:50:52 +04:00
return 0 ;
}
static void hmac_exit_tfm ( struct crypto_tfm * tfm )
{
2009-07-09 08:43:37 +04:00
struct hmac_ctx * ctx = hmac_ctx ( __crypto_shash_cast ( tfm ) ) ;
crypto_free_shash ( ctx - > desc - > tfm ) ;
kzfree ( ctx - > desc ) ;
2006-08-21 14:50:52 +04:00
}
2009-07-09 08:43:37 +04:00
static int hmac_create ( struct crypto_template * tmpl , struct rtattr * * tb )
2006-08-21 14:50:52 +04:00
{
2009-07-09 08:43:37 +04:00
struct shash_instance * inst ;
2006-08-21 14:50:52 +04:00
struct crypto_alg * alg ;
2009-07-09 08:43:37 +04:00
struct shash_alg * salg ;
2007-01-01 10:37:02 +03:00
int err ;
2008-07-07 16:23:56 +04:00
int ds ;
2007-01-01 10:37:02 +03:00
2009-07-09 08:43:37 +04:00
err = crypto_check_attr_type ( tb , CRYPTO_ALG_TYPE_SHASH ) ;
2007-01-01 10:37:02 +03:00
if ( err )
2009-07-09 08:43:37 +04:00
return err ;
salg = shash_attr_alg ( tb [ 1 ] , 0 , 0 ) ;
if ( IS_ERR ( salg ) )
return PTR_ERR ( salg ) ;
err = - EINVAL ;
ds = salg - > digestsize ;
alg = & salg - > base ;
2008-07-07 16:23:56 +04:00
if ( ds > alg - > cra_blocksize )
goto out_put_alg ;
2009-07-09 08:43:37 +04:00
inst = shash_alloc_instance ( " hmac " , alg ) ;
2006-08-21 14:50:52 +04:00
if ( IS_ERR ( inst ) )
goto out_put_alg ;
2009-07-09 08:43:37 +04:00
err = crypto_init_shash_spawn ( shash_instance_ctx ( inst ) , salg ,
shash_crypto_instance ( inst ) ) ;
if ( err )
goto out_free_inst ;
inst - > alg . base . cra_priority = alg - > cra_priority ;
inst - > alg . base . cra_blocksize = alg - > cra_blocksize ;
inst - > alg . base . cra_alignmask = alg - > cra_alignmask ;
2006-08-21 14:50:52 +04:00
2009-07-09 08:43:37 +04:00
inst - > alg . digestsize = ds ;
2006-08-21 14:50:52 +04:00
2009-07-09 08:43:37 +04:00
inst - > alg . base . cra_ctxsize = sizeof ( struct hmac_ctx ) +
ALIGN ( alg - > cra_blocksize * 2 + ds ,
crypto_tfm_ctx_alignment ( ) ) ;
2006-08-21 14:50:52 +04:00
2009-07-09 08:43:37 +04:00
inst - > alg . base . cra_init = hmac_init_tfm ;
inst - > alg . base . cra_exit = hmac_exit_tfm ;
2006-08-21 14:50:52 +04:00
2009-07-09 08:43:37 +04:00
inst - > alg . init = hmac_init ;
inst - > alg . update = hmac_update ;
inst - > alg . final = hmac_final ;
inst - > alg . finup = hmac_finup ;
inst - > alg . setkey = hmac_setkey ;
err = shash_register_instance ( tmpl , inst ) ;
if ( err ) {
out_free_inst :
shash_free_instance ( shash_crypto_instance ( inst ) ) ;
}
2006-08-21 14:50:52 +04:00
out_put_alg :
crypto_mod_put ( alg ) ;
2009-07-09 08:43:37 +04:00
return err ;
2006-08-21 14:50:52 +04:00
}
static struct crypto_template hmac_tmpl = {
. name = " hmac " ,
2009-07-09 08:43:37 +04:00
. create = hmac_create ,
. free = shash_free_instance ,
2006-08-21 14:50:52 +04:00
. module = THIS_MODULE ,
} ;
static int __init hmac_module_init ( void )
{
return crypto_register_template ( & hmac_tmpl ) ;
}
static void __exit hmac_module_exit ( void )
{
crypto_unregister_template ( & hmac_tmpl ) ;
}
module_init ( hmac_module_init ) ;
module_exit ( hmac_module_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " HMAC hash algorithm " ) ;