2005-04-17 02:20:36 +04:00
/*
* Cryptographic API .
*
* Digest operations .
*
* Copyright ( c ) 2002 James Morris < jmorris @ intercode . com . au >
*
* 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 .
*
*/
2006-08-19 16:24:23 +04:00
2007-12-07 13:52:49 +03:00
# include <crypto/scatterwalk.h>
2005-04-17 02:20:36 +04:00
# include <linux/mm.h>
# include <linux/errno.h>
2006-12-10 02:45:28 +03:00
# include <linux/hardirq.h>
2005-04-17 02:20:36 +04:00
# include <linux/highmem.h>
2006-12-10 02:45:28 +03:00
# include <linux/kernel.h>
2006-08-19 16:24:23 +04:00
# include <linux/module.h>
# include <linux/scatterlist.h>
static int init ( struct hash_desc * desc )
{
struct crypto_tfm * tfm = crypto_hash_tfm ( desc - > tfm ) ;
tfm - > __crt_alg - > cra_digest . dia_init ( tfm ) ;
return 0 ;
}
2006-12-10 02:45:28 +03:00
static int update2 ( struct hash_desc * desc ,
struct scatterlist * sg , unsigned int nbytes )
2006-08-19 16:24:23 +04:00
{
struct crypto_tfm * tfm = crypto_hash_tfm ( desc - > tfm ) ;
2006-04-10 02:42:35 +04:00
unsigned int alignmask = crypto_tfm_alg_alignmask ( tfm ) ;
2005-04-17 02:20:36 +04:00
2006-08-19 16:24:23 +04:00
if ( ! nbytes )
return 0 ;
for ( ; ; ) {
2007-10-22 21:40:16 +04:00
struct page * pg = sg_page ( sg ) ;
2006-08-19 16:24:23 +04:00
unsigned int offset = sg - > offset ;
unsigned int l = sg - > length ;
2005-04-17 02:20:36 +04:00
2006-08-19 16:24:23 +04:00
if ( unlikely ( l > nbytes ) )
l = nbytes ;
nbytes - = l ;
2005-04-17 02:20:36 +04:00
do {
unsigned int bytes_from_page = min ( l , ( ( unsigned int )
( PAGE_SIZE ) ) -
offset ) ;
2006-04-10 02:42:35 +04:00
char * src = crypto_kmap ( pg , 0 ) ;
char * p = src + offset ;
2005-04-17 02:20:36 +04:00
2006-04-10 02:42:35 +04:00
if ( unlikely ( offset & alignmask ) ) {
unsigned int bytes =
alignmask + 1 - ( offset & alignmask ) ;
bytes = min ( bytes , bytes_from_page ) ;
2006-05-16 16:09:29 +04:00
tfm - > __crt_alg - > cra_digest . dia_update ( tfm , p ,
bytes ) ;
2006-04-10 02:42:35 +04:00
p + = bytes ;
bytes_from_page - = bytes ;
l - = bytes ;
}
2006-05-16 16:09:29 +04:00
tfm - > __crt_alg - > cra_digest . dia_update ( tfm , p ,
bytes_from_page ) ;
2006-04-10 02:42:35 +04:00
crypto_kunmap ( src , 0 ) ;
2006-08-19 16:24:23 +04:00
crypto_yield ( desc - > flags ) ;
2005-04-17 02:20:36 +04:00
offset = 0 ;
pg + + ;
l - = bytes_from_page ;
} while ( l > 0 ) ;
2006-08-19 16:24:23 +04:00
if ( ! nbytes )
break ;
2007-12-05 12:59:25 +03:00
sg = scatterwalk_sg_next ( sg ) ;
2005-04-17 02:20:36 +04:00
}
2006-08-19 16:24:23 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-12-10 02:45:28 +03:00
static int update ( struct hash_desc * desc ,
struct scatterlist * sg , unsigned int nbytes )
{
if ( WARN_ON_ONCE ( in_irq ( ) ) )
return - EDEADLK ;
return update2 ( desc , sg , nbytes ) ;
}
2006-08-19 16:24:23 +04:00
static int final ( struct hash_desc * desc , u8 * out )
2005-04-17 02:20:36 +04:00
{
2006-08-19 16:24:23 +04:00
struct crypto_tfm * tfm = crypto_hash_tfm ( desc - > tfm ) ;
2006-04-10 02:42:35 +04:00
unsigned long alignmask = crypto_tfm_alg_alignmask ( tfm ) ;
2006-07-09 08:49:42 +04:00
struct digest_alg * digest = & tfm - > __crt_alg - > cra_digest ;
2006-04-10 02:42:35 +04:00
if ( unlikely ( ( unsigned long ) out & alignmask ) ) {
2006-07-09 08:49:42 +04:00
unsigned long align = alignmask + 1 ;
unsigned long addr = ( unsigned long ) crypto_tfm_ctx ( tfm ) ;
u8 * dst = ( u8 * ) ALIGN ( addr , align ) +
ALIGN ( tfm - > __crt_alg - > cra_ctxsize , align ) ;
digest - > dia_final ( tfm , dst ) ;
memcpy ( out , dst , digest - > dia_digestsize ) ;
2006-04-10 02:42:35 +04:00
} else
2006-07-09 08:49:42 +04:00
digest - > dia_final ( tfm , out ) ;
2006-08-19 16:24:23 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-08-19 16:24:23 +04:00
static int nosetkey ( struct crypto_hash * tfm , const u8 * key , unsigned int keylen )
2006-08-13 08:16:39 +04:00
{
2006-08-19 16:24:23 +04:00
crypto_hash_clear_flags ( tfm , CRYPTO_TFM_RES_MASK ) ;
2006-08-13 08:16:39 +04:00
return - ENOSYS ;
}
2006-08-19 16:24:23 +04:00
static int setkey ( struct crypto_hash * hash , const u8 * key , unsigned int keylen )
2005-04-17 02:20:36 +04:00
{
2006-08-19 16:24:23 +04:00
struct crypto_tfm * tfm = crypto_hash_tfm ( hash ) ;
crypto_hash_clear_flags ( hash , CRYPTO_TFM_RES_MASK ) ;
2006-08-13 08:16:39 +04:00
return tfm - > __crt_alg - > cra_digest . dia_setkey ( tfm , key , keylen ) ;
2005-04-17 02:20:36 +04:00
}
2006-08-19 16:24:23 +04:00
static int digest ( struct hash_desc * desc ,
struct scatterlist * sg , unsigned int nbytes , u8 * out )
2005-04-17 02:20:36 +04:00
{
2006-12-10 02:45:28 +03:00
if ( WARN_ON_ONCE ( in_irq ( ) ) )
return - EDEADLK ;
2006-08-19 16:24:23 +04:00
init ( desc ) ;
2006-12-10 02:45:28 +03:00
update2 ( desc , sg , nbytes ) ;
2006-08-19 16:24:23 +04:00
return final ( desc , out ) ;
2005-04-17 02:20:36 +04:00
}
int crypto_init_digest_ops ( struct crypto_tfm * tfm )
{
2006-08-19 16:24:23 +04:00
struct hash_tfm * ops = & tfm - > crt_hash ;
2006-08-13 08:16:39 +04:00
struct digest_alg * dalg = & tfm - > __crt_alg - > cra_digest ;
2006-08-19 16:24:23 +04:00
if ( dalg - > dia_digestsize > crypto_tfm_alg_blocksize ( tfm ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-08-19 16:24:23 +04:00
ops - > init = init ;
ops - > update = update ;
ops - > final = final ;
ops - > digest = digest ;
ops - > setkey = dalg - > dia_setkey ? setkey : nosetkey ;
ops - > digestsize = dalg - > dia_digestsize ;
2005-04-17 02:20:36 +04:00
2006-08-20 09:25:22 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
void crypto_exit_digest_ops ( struct crypto_tfm * tfm )
{
}