2005-04-17 02:20:36 +04:00
/* SHA-512 code by Jean-Luc Cooke <jlcooke@certainkey.com>
*
* Copyright ( c ) Jean - Luc Cooke < jlcooke @ certainkey . com >
* Copyright ( c ) Andrew McDonald < andrew @ mcdonald . org . uk >
* Copyright ( c ) 2003 Kyle McMartin < kyle @ debian . org >
*
* 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 , or ( at your option ) any
* later version .
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/mm.h>
# include <linux/init.h>
# include <linux/crypto.h>
2005-10-30 13:25:15 +03:00
# include <linux/types.h>
2007-10-09 18:43:13 +04:00
# include <crypto/sha.h>
2008-12-17 08:47:52 +03:00
# include <linux/percpu.h>
2005-04-17 02:20:36 +04:00
# include <asm/byteorder.h>
struct sha512_ctx {
u64 state [ 8 ] ;
u32 count [ 4 ] ;
u8 buf [ 128 ] ;
} ;
2008-12-17 08:47:52 +03:00
static DEFINE_PER_CPU ( u64 [ 80 ] , msg_schedule ) ;
2005-04-17 02:20:36 +04:00
static inline u64 Ch ( u64 x , u64 y , u64 z )
{
return z ^ ( x & ( y ^ z ) ) ;
}
static inline u64 Maj ( u64 x , u64 y , u64 z )
{
return ( x & y ) | ( z & ( x | y ) ) ;
}
static inline u64 RORu64 ( u64 x , u64 y )
{
return ( x > > y ) | ( x < < ( 64 - y ) ) ;
}
static const u64 sha512_K [ 80 ] = {
0x428a2f98d728ae22ULL , 0x7137449123ef65cdULL , 0xb5c0fbcfec4d3b2fULL ,
0xe9b5dba58189dbbcULL , 0x3956c25bf348b538ULL , 0x59f111f1b605d019ULL ,
0x923f82a4af194f9bULL , 0xab1c5ed5da6d8118ULL , 0xd807aa98a3030242ULL ,
0x12835b0145706fbeULL , 0x243185be4ee4b28cULL , 0x550c7dc3d5ffb4e2ULL ,
0x72be5d74f27b896fULL , 0x80deb1fe3b1696b1ULL , 0x9bdc06a725c71235ULL ,
0xc19bf174cf692694ULL , 0xe49b69c19ef14ad2ULL , 0xefbe4786384f25e3ULL ,
0x0fc19dc68b8cd5b5ULL , 0x240ca1cc77ac9c65ULL , 0x2de92c6f592b0275ULL ,
0x4a7484aa6ea6e483ULL , 0x5cb0a9dcbd41fbd4ULL , 0x76f988da831153b5ULL ,
0x983e5152ee66dfabULL , 0xa831c66d2db43210ULL , 0xb00327c898fb213fULL ,
0xbf597fc7beef0ee4ULL , 0xc6e00bf33da88fc2ULL , 0xd5a79147930aa725ULL ,
0x06ca6351e003826fULL , 0x142929670a0e6e70ULL , 0x27b70a8546d22ffcULL ,
0x2e1b21385c26c926ULL , 0x4d2c6dfc5ac42aedULL , 0x53380d139d95b3dfULL ,
0x650a73548baf63deULL , 0x766a0abb3c77b2a8ULL , 0x81c2c92e47edaee6ULL ,
0x92722c851482353bULL , 0xa2bfe8a14cf10364ULL , 0xa81a664bbc423001ULL ,
0xc24b8b70d0f89791ULL , 0xc76c51a30654be30ULL , 0xd192e819d6ef5218ULL ,
0xd69906245565a910ULL , 0xf40e35855771202aULL , 0x106aa07032bbd1b8ULL ,
0x19a4c116b8d2d0c8ULL , 0x1e376c085141ab53ULL , 0x2748774cdf8eeb99ULL ,
0x34b0bcb5e19b48a8ULL , 0x391c0cb3c5c95a63ULL , 0x4ed8aa4ae3418acbULL ,
0x5b9cca4f7763e373ULL , 0x682e6ff3d6b2b8a3ULL , 0x748f82ee5defb2fcULL ,
0x78a5636f43172f60ULL , 0x84c87814a1f0ab72ULL , 0x8cc702081a6439ecULL ,
0x90befffa23631e28ULL , 0xa4506cebde82bde9ULL , 0xbef9a3f7b2c67915ULL ,
0xc67178f2e372532bULL , 0xca273eceea26619cULL , 0xd186b8c721c0c207ULL ,
0xeada7dd6cde0eb1eULL , 0xf57d4f7fee6ed178ULL , 0x06f067aa72176fbaULL ,
0x0a637dc5a2c898a6ULL , 0x113f9804bef90daeULL , 0x1b710b35131c471bULL ,
0x28db77f523047d84ULL , 0x32caab7b40c72493ULL , 0x3c9ebe0a15c9bebcULL ,
0x431d67c49c100d4cULL , 0x4cc5d4becb3e42b6ULL , 0x597f299cfc657e2aULL ,
0x5fcb6fab3ad6faecULL , 0x6c44198c4a475817ULL ,
} ;
# define e0(x) (RORu64(x,28) ^ RORu64(x,34) ^ RORu64(x,39))
# define e1(x) (RORu64(x,14) ^ RORu64(x,18) ^ RORu64(x,41))
# define s0(x) (RORu64(x, 1) ^ RORu64(x, 8) ^ (x >> 7))
# define s1(x) (RORu64(x,19) ^ RORu64(x,61) ^ (x >> 6))
static inline void LOAD_OP ( int I , u64 * W , const u8 * input )
{
W [ I ] = __be64_to_cpu ( ( ( __be64 * ) ( input ) ) [ I ] ) ;
}
static inline void BLEND_OP ( int I , u64 * W )
{
W [ I ] = s1 ( W [ I - 2 ] ) + W [ I - 7 ] + s0 ( W [ I - 15 ] ) + W [ I - 16 ] ;
}
static void
2008-12-17 08:47:52 +03:00
sha512_transform ( u64 * state , const u8 * input )
2005-04-17 02:20:36 +04:00
{
u64 a , b , c , d , e , f , g , h , t1 , t2 ;
int i ;
2008-12-17 08:47:52 +03:00
u64 * W = get_cpu_var ( msg_schedule ) ;
2005-04-17 02:20:36 +04:00
/* load the input */
for ( i = 0 ; i < 16 ; i + + )
LOAD_OP ( i , W , input ) ;
for ( i = 16 ; i < 80 ; i + + ) {
BLEND_OP ( i , W ) ;
}
/* load the state into our registers */
2008-03-06 14:55:38 +03:00
a = state [ 0 ] ; b = state [ 1 ] ; c = state [ 2 ] ; d = state [ 3 ] ;
e = state [ 4 ] ; f = state [ 5 ] ; g = state [ 6 ] ; h = state [ 7 ] ;
2005-04-17 02:20:36 +04:00
/* now iterate */
for ( i = 0 ; i < 80 ; i + = 8 ) {
t1 = h + e1 ( e ) + Ch ( e , f , g ) + sha512_K [ i ] + W [ i ] ;
t2 = e0 ( a ) + Maj ( a , b , c ) ; d + = t1 ; h = t1 + t2 ;
t1 = g + e1 ( d ) + Ch ( d , e , f ) + sha512_K [ i + 1 ] + W [ i + 1 ] ;
t2 = e0 ( h ) + Maj ( h , a , b ) ; c + = t1 ; g = t1 + t2 ;
t1 = f + e1 ( c ) + Ch ( c , d , e ) + sha512_K [ i + 2 ] + W [ i + 2 ] ;
t2 = e0 ( g ) + Maj ( g , h , a ) ; b + = t1 ; f = t1 + t2 ;
t1 = e + e1 ( b ) + Ch ( b , c , d ) + sha512_K [ i + 3 ] + W [ i + 3 ] ;
t2 = e0 ( f ) + Maj ( f , g , h ) ; a + = t1 ; e = t1 + t2 ;
t1 = d + e1 ( a ) + Ch ( a , b , c ) + sha512_K [ i + 4 ] + W [ i + 4 ] ;
t2 = e0 ( e ) + Maj ( e , f , g ) ; h + = t1 ; d = t1 + t2 ;
t1 = c + e1 ( h ) + Ch ( h , a , b ) + sha512_K [ i + 5 ] + W [ i + 5 ] ;
t2 = e0 ( d ) + Maj ( d , e , f ) ; g + = t1 ; c = t1 + t2 ;
t1 = b + e1 ( g ) + Ch ( g , h , a ) + sha512_K [ i + 6 ] + W [ i + 6 ] ;
t2 = e0 ( c ) + Maj ( c , d , e ) ; f + = t1 ; b = t1 + t2 ;
t1 = a + e1 ( f ) + Ch ( f , g , h ) + sha512_K [ i + 7 ] + W [ i + 7 ] ;
t2 = e0 ( b ) + Maj ( b , c , d ) ; e + = t1 ; a = t1 + t2 ;
}
2008-03-06 14:55:38 +03:00
state [ 0 ] + = a ; state [ 1 ] + = b ; state [ 2 ] + = c ; state [ 3 ] + = d ;
state [ 4 ] + = e ; state [ 5 ] + = f ; state [ 6 ] + = g ; state [ 7 ] + = h ;
2005-04-17 02:20:36 +04:00
/* erase our data */
a = b = c = d = e = f = g = h = t1 = t2 = 0 ;
2008-12-17 08:47:52 +03:00
memset ( W , 0 , sizeof ( __get_cpu_var ( msg_schedule ) ) ) ;
put_cpu_var ( msg_schedule ) ;
2005-04-17 02:20:36 +04:00
}
static void
2006-05-16 16:09:29 +04:00
sha512_init ( struct crypto_tfm * tfm )
2005-04-17 02:20:36 +04:00
{
2006-05-16 16:09:29 +04:00
struct sha512_ctx * sctx = crypto_tfm_ctx ( tfm ) ;
2007-10-09 18:43:13 +04:00
sctx - > state [ 0 ] = SHA512_H0 ;
sctx - > state [ 1 ] = SHA512_H1 ;
sctx - > state [ 2 ] = SHA512_H2 ;
sctx - > state [ 3 ] = SHA512_H3 ;
sctx - > state [ 4 ] = SHA512_H4 ;
sctx - > state [ 5 ] = SHA512_H5 ;
sctx - > state [ 6 ] = SHA512_H6 ;
sctx - > state [ 7 ] = SHA512_H7 ;
2005-04-17 02:20:36 +04:00
sctx - > count [ 0 ] = sctx - > count [ 1 ] = sctx - > count [ 2 ] = sctx - > count [ 3 ] = 0 ;
}
static void
2006-05-16 16:09:29 +04:00
sha384_init ( struct crypto_tfm * tfm )
2005-04-17 02:20:36 +04:00
{
2006-05-16 16:09:29 +04:00
struct sha512_ctx * sctx = crypto_tfm_ctx ( tfm ) ;
2007-10-09 18:43:13 +04:00
sctx - > state [ 0 ] = SHA384_H0 ;
sctx - > state [ 1 ] = SHA384_H1 ;
sctx - > state [ 2 ] = SHA384_H2 ;
sctx - > state [ 3 ] = SHA384_H3 ;
sctx - > state [ 4 ] = SHA384_H4 ;
sctx - > state [ 5 ] = SHA384_H5 ;
sctx - > state [ 6 ] = SHA384_H6 ;
sctx - > state [ 7 ] = SHA384_H7 ;
2005-04-17 02:20:36 +04:00
sctx - > count [ 0 ] = sctx - > count [ 1 ] = sctx - > count [ 2 ] = sctx - > count [ 3 ] = 0 ;
}
static void
2006-05-16 16:09:29 +04:00
sha512_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 sha512_ctx * sctx = crypto_tfm_ctx ( tfm ) ;
2005-04-17 02:20:36 +04:00
unsigned int i , index , part_len ;
/* Compute number of bytes mod 128 */
index = ( unsigned int ) ( ( sctx - > count [ 0 ] > > 3 ) & 0x7F ) ;
2008-03-06 14:55:38 +03:00
2005-04-17 02:20:36 +04:00
/* Update number of bits */
if ( ( sctx - > count [ 0 ] + = ( len < < 3 ) ) < ( len < < 3 ) ) {
if ( ( sctx - > count [ 1 ] + = 1 ) < 1 )
if ( ( sctx - > count [ 2 ] + = 1 ) < 1 )
sctx - > count [ 3 ] + + ;
sctx - > count [ 1 ] + = ( len > > 29 ) ;
}
2008-03-06 14:55:38 +03:00
2005-04-17 02:20:36 +04:00
part_len = 128 - index ;
2008-03-06 14:55:38 +03:00
2005-04-17 02:20:36 +04:00
/* Transform as many times as possible. */
if ( len > = part_len ) {
memcpy ( & sctx - > buf [ index ] , data , part_len ) ;
2008-12-17 08:47:52 +03:00
sha512_transform ( sctx - > state , sctx - > buf ) ;
2005-04-17 02:20:36 +04:00
for ( i = part_len ; i + 127 < len ; i + = 128 )
2008-12-17 08:47:52 +03:00
sha512_transform ( sctx - > state , & data [ i ] ) ;
2005-04-17 02:20:36 +04:00
index = 0 ;
} else {
i = 0 ;
}
/* Buffer remaining input */
memcpy ( & sctx - > buf [ index ] , & data [ i ] , len - i ) ;
}
static void
2006-05-16 16:09:29 +04:00
sha512_final ( struct crypto_tfm * tfm , u8 * hash )
2005-04-17 02:20:36 +04:00
{
2006-05-16 16:09:29 +04:00
struct sha512_ctx * sctx = crypto_tfm_ctx ( tfm ) ;
2005-04-17 02:20:36 +04:00
static u8 padding [ 128 ] = { 0x80 , } ;
2005-10-30 13:25:15 +03:00
__be64 * dst = ( __be64 * ) hash ;
__be32 bits [ 4 ] ;
2005-04-17 02:20:36 +04:00
unsigned int index , pad_len ;
2005-10-30 13:25:15 +03:00
int i ;
2005-04-17 02:20:36 +04:00
/* Save number of bits */
2005-10-30 13:25:15 +03:00
bits [ 3 ] = cpu_to_be32 ( sctx - > count [ 0 ] ) ;
bits [ 2 ] = cpu_to_be32 ( sctx - > count [ 1 ] ) ;
bits [ 1 ] = cpu_to_be32 ( sctx - > count [ 2 ] ) ;
bits [ 0 ] = cpu_to_be32 ( sctx - > count [ 3 ] ) ;
2005-04-17 02:20:36 +04:00
/* Pad out to 112 mod 128. */
index = ( sctx - > count [ 0 ] > > 3 ) & 0x7f ;
pad_len = ( index < 112 ) ? ( 112 - index ) : ( ( 128 + 112 ) - index ) ;
2006-05-16 16:09:29 +04:00
sha512_update ( tfm , padding , pad_len ) ;
2005-04-17 02:20:36 +04:00
/* Append length (before padding) */
2006-05-16 16:09:29 +04:00
sha512_update ( tfm , ( const u8 * ) bits , sizeof ( bits ) ) ;
2005-04-17 02:20:36 +04:00
/* Store state in digest */
2005-10-30 13:25:15 +03:00
for ( i = 0 ; i < 8 ; i + + )
dst [ i ] = cpu_to_be64 ( sctx - > state [ i ] ) ;
2005-04-17 02:20:36 +04:00
/* Zeroize sensitive information. */
memset ( sctx , 0 , sizeof ( struct sha512_ctx ) ) ;
}
2006-05-16 16:09:29 +04:00
static void sha384_final ( struct crypto_tfm * tfm , u8 * hash )
2005-04-17 02:20:36 +04:00
{
u8 D [ 64 ] ;
2006-05-16 16:09:29 +04:00
sha512_final ( tfm , D ) ;
2005-04-17 02:20:36 +04:00
memcpy ( hash , D , 48 ) ;
memset ( D , 0 , 64 ) ;
}
static struct crypto_alg sha512 = {
. cra_name = " sha512 " ,
. cra_flags = CRYPTO_ALG_TYPE_DIGEST ,
2007-10-09 18:43:13 +04:00
. cra_blocksize = SHA512_BLOCK_SIZE ,
2005-04-17 02:20:36 +04:00
. cra_ctxsize = sizeof ( struct sha512_ctx ) ,
. cra_module = THIS_MODULE ,
2006-04-10 02:42:35 +04:00
. cra_alignmask = 3 ,
2005-04-17 02:20:36 +04:00
. cra_list = LIST_HEAD_INIT ( sha512 . cra_list ) ,
. cra_u = { . digest = {
. dia_digestsize = SHA512_DIGEST_SIZE ,
. dia_init = sha512_init ,
. dia_update = sha512_update ,
. dia_final = sha512_final }
}
} ;
static struct crypto_alg sha384 = {
. cra_name = " sha384 " ,
. cra_flags = CRYPTO_ALG_TYPE_DIGEST ,
2007-10-09 18:43:13 +04:00
. cra_blocksize = SHA384_BLOCK_SIZE ,
2005-04-17 02:20:36 +04:00
. cra_ctxsize = sizeof ( struct sha512_ctx ) ,
2006-04-10 02:42:35 +04:00
. cra_alignmask = 3 ,
2005-04-17 02:20:36 +04:00
. cra_module = THIS_MODULE ,
. cra_list = LIST_HEAD_INIT ( sha384 . cra_list ) ,
. cra_u = { . digest = {
. dia_digestsize = SHA384_DIGEST_SIZE ,
. dia_init = sha384_init ,
. dia_update = sha512_update ,
. dia_final = sha384_final }
}
} ;
2008-04-05 17:00:57 +04:00
static int __init sha512_generic_mod_init ( void )
2005-04-17 02:20:36 +04:00
{
int ret = 0 ;
if ( ( ret = crypto_register_alg ( & sha384 ) ) < 0 )
goto out ;
if ( ( ret = crypto_register_alg ( & sha512 ) ) < 0 )
crypto_unregister_alg ( & sha384 ) ;
out :
return ret ;
}
2008-04-05 17:00:57 +04:00
static void __exit sha512_generic_mod_fini ( void )
2005-04-17 02:20:36 +04:00
{
crypto_unregister_alg ( & sha384 ) ;
crypto_unregister_alg ( & sha512 ) ;
}
2008-04-05 17:00:57 +04:00
module_init ( sha512_generic_mod_init ) ;
module_exit ( sha512_generic_mod_fini ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " SHA-512 and SHA-384 Secure Hash Algorithms " ) ;
2008-03-06 14:55:38 +03:00
MODULE_ALIAS ( " sha384 " ) ;
MODULE_ALIAS ( " sha512 " ) ;