2005-04-17 02:20:36 +04:00
/*
* Cryptographic API .
*
* MD4 Message Digest Algorithm ( RFC1320 ) .
*
* Implementation derived from Andrew Tridgell and Steve French ' s
* CIFS MD4 implementation , and the cryptoapi implementation
* originally based on the public domain implementation written
* by Colin Plumb in 1993.
*
* Copyright ( c ) Andrew Tridgell 1997 - 1998.
* Modified by Steve French ( sfrench @ us . ibm . com ) 2002
* Copyright ( c ) Cryptoapi developers .
* Copyright ( c ) 2002 David S . Miller ( davem @ redhat . com )
* 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 .
*
*/
# include <linux/init.h>
# include <linux/crypto.h>
# include <linux/kernel.h>
# include <linux/string.h>
2005-10-30 13:25:15 +03:00
# include <linux/types.h>
2005-04-17 02:20:36 +04:00
# include <asm/byteorder.h>
# define MD4_DIGEST_SIZE 16
# define MD4_HMAC_BLOCK_SIZE 64
# define MD4_BLOCK_WORDS 16
# define MD4_HASH_WORDS 4
struct md4_ctx {
u32 hash [ MD4_HASH_WORDS ] ;
u32 block [ MD4_BLOCK_WORDS ] ;
u64 byte_count ;
} ;
static inline u32 lshift ( u32 x , unsigned int s )
{
x & = 0xFFFFFFFF ;
return ( ( x < < s ) & 0xFFFFFFFF ) | ( x > > ( 32 - s ) ) ;
}
static inline u32 F ( u32 x , u32 y , u32 z )
{
return ( x & y ) | ( ( ~ x ) & z ) ;
}
static inline u32 G ( u32 x , u32 y , u32 z )
{
return ( x & y ) | ( x & z ) | ( y & z ) ;
}
static inline u32 H ( u32 x , u32 y , u32 z )
{
return x ^ y ^ z ;
}
# define ROUND1(a,b,c,d,k,s) (a = lshift(a + F(b,c,d) + k, s))
# define ROUND2(a,b,c,d,k,s) (a = lshift(a + G(b,c,d) + k + (u32)0x5A827999,s))
# define ROUND3(a,b,c,d,k,s) (a = lshift(a + H(b,c,d) + k + (u32)0x6ED9EBA1,s))
/* XXX: this stuff can be optimized */
static inline void le32_to_cpu_array ( u32 * buf , unsigned int words )
{
while ( words - - ) {
__le32_to_cpus ( buf ) ;
buf + + ;
}
}
static inline void cpu_to_le32_array ( u32 * buf , unsigned int words )
{
while ( words - - ) {
__cpu_to_le32s ( buf ) ;
buf + + ;
}
}
static void md4_transform ( u32 * hash , u32 const * in )
{
u32 a , b , c , d ;
a = hash [ 0 ] ;
b = hash [ 1 ] ;
c = hash [ 2 ] ;
d = hash [ 3 ] ;
ROUND1 ( a , b , c , d , in [ 0 ] , 3 ) ;
ROUND1 ( d , a , b , c , in [ 1 ] , 7 ) ;
ROUND1 ( c , d , a , b , in [ 2 ] , 11 ) ;
ROUND1 ( b , c , d , a , in [ 3 ] , 19 ) ;
ROUND1 ( a , b , c , d , in [ 4 ] , 3 ) ;
ROUND1 ( d , a , b , c , in [ 5 ] , 7 ) ;
ROUND1 ( c , d , a , b , in [ 6 ] , 11 ) ;
ROUND1 ( b , c , d , a , in [ 7 ] , 19 ) ;
ROUND1 ( a , b , c , d , in [ 8 ] , 3 ) ;
ROUND1 ( d , a , b , c , in [ 9 ] , 7 ) ;
ROUND1 ( c , d , a , b , in [ 10 ] , 11 ) ;
ROUND1 ( b , c , d , a , in [ 11 ] , 19 ) ;
ROUND1 ( a , b , c , d , in [ 12 ] , 3 ) ;
ROUND1 ( d , a , b , c , in [ 13 ] , 7 ) ;
ROUND1 ( c , d , a , b , in [ 14 ] , 11 ) ;
ROUND1 ( b , c , d , a , in [ 15 ] , 19 ) ;
ROUND2 ( a , b , c , d , in [ 0 ] , 3 ) ;
ROUND2 ( d , a , b , c , in [ 4 ] , 5 ) ;
ROUND2 ( c , d , a , b , in [ 8 ] , 9 ) ;
ROUND2 ( b , c , d , a , in [ 12 ] , 13 ) ;
ROUND2 ( a , b , c , d , in [ 1 ] , 3 ) ;
ROUND2 ( d , a , b , c , in [ 5 ] , 5 ) ;
ROUND2 ( c , d , a , b , in [ 9 ] , 9 ) ;
ROUND2 ( b , c , d , a , in [ 13 ] , 13 ) ;
ROUND2 ( a , b , c , d , in [ 2 ] , 3 ) ;
ROUND2 ( d , a , b , c , in [ 6 ] , 5 ) ;
ROUND2 ( c , d , a , b , in [ 10 ] , 9 ) ;
ROUND2 ( b , c , d , a , in [ 14 ] , 13 ) ;
ROUND2 ( a , b , c , d , in [ 3 ] , 3 ) ;
ROUND2 ( d , a , b , c , in [ 7 ] , 5 ) ;
ROUND2 ( c , d , a , b , in [ 11 ] , 9 ) ;
ROUND2 ( b , c , d , a , in [ 15 ] , 13 ) ;
ROUND3 ( a , b , c , d , in [ 0 ] , 3 ) ;
ROUND3 ( d , a , b , c , in [ 8 ] , 9 ) ;
ROUND3 ( c , d , a , b , in [ 4 ] , 11 ) ;
ROUND3 ( b , c , d , a , in [ 12 ] , 15 ) ;
ROUND3 ( a , b , c , d , in [ 2 ] , 3 ) ;
ROUND3 ( d , a , b , c , in [ 10 ] , 9 ) ;
ROUND3 ( c , d , a , b , in [ 6 ] , 11 ) ;
ROUND3 ( b , c , d , a , in [ 14 ] , 15 ) ;
ROUND3 ( a , b , c , d , in [ 1 ] , 3 ) ;
ROUND3 ( d , a , b , c , in [ 9 ] , 9 ) ;
ROUND3 ( c , d , a , b , in [ 5 ] , 11 ) ;
ROUND3 ( b , c , d , a , in [ 13 ] , 15 ) ;
ROUND3 ( a , b , c , d , in [ 3 ] , 3 ) ;
ROUND3 ( d , a , b , c , in [ 11 ] , 9 ) ;
ROUND3 ( c , d , a , b , in [ 7 ] , 11 ) ;
ROUND3 ( b , c , d , a , in [ 15 ] , 15 ) ;
hash [ 0 ] + = a ;
hash [ 1 ] + = b ;
hash [ 2 ] + = c ;
hash [ 3 ] + = d ;
}
static inline void md4_transform_helper ( struct md4_ctx * ctx )
{
le32_to_cpu_array ( ctx - > block , sizeof ( ctx - > block ) / sizeof ( u32 ) ) ;
md4_transform ( ctx - > hash , ctx - > block ) ;
}
2006-05-16 16:09:29 +04:00
static void md4_init ( struct crypto_tfm * tfm )
2005-04-17 02:20:36 +04:00
{
2006-05-16 16:09:29 +04:00
struct md4_ctx * mctx = crypto_tfm_ctx ( tfm ) ;
2005-04-17 02:20:36 +04:00
mctx - > hash [ 0 ] = 0x67452301 ;
mctx - > hash [ 1 ] = 0xefcdab89 ;
mctx - > hash [ 2 ] = 0x98badcfe ;
mctx - > hash [ 3 ] = 0x10325476 ;
mctx - > byte_count = 0 ;
}
2006-05-16 16:09:29 +04:00
static void md4_update ( struct crypto_tfm * tfm , const u8 * data , unsigned int len )
2005-04-17 02:20:36 +04:00
{
2006-05-16 16:09:29 +04:00
struct md4_ctx * mctx = crypto_tfm_ctx ( tfm ) ;
2005-04-17 02:20:36 +04:00
const u32 avail = sizeof ( mctx - > block ) - ( mctx - > byte_count & 0x3f ) ;
mctx - > byte_count + = len ;
if ( avail > len ) {
memcpy ( ( char * ) mctx - > block + ( sizeof ( mctx - > block ) - avail ) ,
data , len ) ;
return ;
}
memcpy ( ( char * ) mctx - > block + ( sizeof ( mctx - > block ) - avail ) ,
data , avail ) ;
md4_transform_helper ( mctx ) ;
data + = avail ;
len - = avail ;
while ( len > = sizeof ( mctx - > block ) ) {
memcpy ( mctx - > block , data , sizeof ( mctx - > block ) ) ;
md4_transform_helper ( mctx ) ;
data + = sizeof ( mctx - > block ) ;
len - = sizeof ( mctx - > block ) ;
}
memcpy ( mctx - > block , data , len ) ;
}
2006-05-16 16:09:29 +04:00
static void md4_final ( struct crypto_tfm * tfm , u8 * out )
2005-04-17 02:20:36 +04:00
{
2006-05-16 16:09:29 +04:00
struct md4_ctx * mctx = crypto_tfm_ctx ( tfm ) ;
2005-04-17 02:20:36 +04:00
const unsigned int offset = mctx - > byte_count & 0x3f ;
char * p = ( char * ) mctx - > block + offset ;
int padding = 56 - ( offset + 1 ) ;
* p + + = 0x80 ;
if ( padding < 0 ) {
memset ( p , 0x00 , padding + sizeof ( u64 ) ) ;
md4_transform_helper ( mctx ) ;
p = ( char * ) mctx - > block ;
padding = 56 ;
}
memset ( p , 0 , padding ) ;
mctx - > block [ 14 ] = mctx - > byte_count < < 3 ;
mctx - > block [ 15 ] = mctx - > byte_count > > 29 ;
le32_to_cpu_array ( mctx - > block , ( sizeof ( mctx - > block ) -
sizeof ( u64 ) ) / sizeof ( u32 ) ) ;
md4_transform ( mctx - > hash , mctx - > block ) ;
cpu_to_le32_array ( mctx - > hash , sizeof ( mctx - > hash ) / sizeof ( u32 ) ) ;
memcpy ( out , mctx - > hash , sizeof ( mctx - > hash ) ) ;
memset ( mctx , 0 , sizeof ( * mctx ) ) ;
}
static struct crypto_alg alg = {
. cra_name = " md4 " ,
. cra_flags = CRYPTO_ALG_TYPE_DIGEST ,
. cra_blocksize = MD4_HMAC_BLOCK_SIZE ,
. cra_ctxsize = sizeof ( struct md4_ctx ) ,
. cra_module = THIS_MODULE ,
. cra_list = LIST_HEAD_INIT ( alg . cra_list ) ,
. cra_u = { . digest = {
. dia_digestsize = MD4_DIGEST_SIZE ,
. dia_init = md4_init ,
. dia_update = md4_update ,
. dia_final = md4_final } }
} ;
static int __init init ( void )
{
return crypto_register_alg ( & alg ) ;
}
static void __exit fini ( void )
{
crypto_unregister_alg ( & alg ) ;
}
module_init ( init ) ;
module_exit ( fini ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " MD4 Message Digest Algorithm " ) ;