2018-05-11 15:19:09 +03:00
/*
* The MORUS - 640 Authenticated - Encryption Algorithm
*
* Copyright ( c ) 2016 - 2018 Ondrej Mosnacek < omosnacek @ gmail . com >
* Copyright ( C ) 2017 - 2018 Red Hat , Inc . All rights reserved .
*
* 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 <asm/unaligned.h>
# include <crypto/algapi.h>
# include <crypto/internal/aead.h>
# include <crypto/internal/skcipher.h>
# include <crypto/morus_common.h>
# include <crypto/scatterwalk.h>
# include <linux/err.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/scatterlist.h>
# define MORUS640_WORD_SIZE 4
# define MORUS640_BLOCK_SIZE (MORUS_BLOCK_WORDS * MORUS640_WORD_SIZE)
# define MORUS640_BLOCK_ALIGN (__alignof__(__le32))
# define MORUS640_ALIGNED(p) IS_ALIGNED((uintptr_t)p, MORUS640_BLOCK_ALIGN)
struct morus640_block {
u32 words [ MORUS_BLOCK_WORDS ] ;
} ;
union morus640_block_in {
__le32 words [ MORUS_BLOCK_WORDS ] ;
u8 bytes [ MORUS640_BLOCK_SIZE ] ;
} ;
struct morus640_state {
struct morus640_block s [ MORUS_STATE_BLOCKS ] ;
} ;
struct morus640_ctx {
struct morus640_block key ;
} ;
struct morus640_ops {
int ( * skcipher_walk_init ) ( struct skcipher_walk * walk ,
struct aead_request * req , bool atomic ) ;
void ( * crypt_chunk ) ( struct morus640_state * state ,
u8 * dst , const u8 * src , unsigned int size ) ;
} ;
static const struct morus640_block crypto_morus640_const [ 2 ] = {
{ . words = {
U32_C ( 0x02010100 ) ,
U32_C ( 0x0d080503 ) ,
U32_C ( 0x59372215 ) ,
U32_C ( 0x6279e990 ) ,
} } ,
{ . words = {
U32_C ( 0x55183ddb ) ,
U32_C ( 0xf12fc26d ) ,
U32_C ( 0x42311120 ) ,
U32_C ( 0xdd28b573 ) ,
} } ,
} ;
static void crypto_morus640_round ( struct morus640_block * b0 ,
struct morus640_block * b1 ,
struct morus640_block * b2 ,
struct morus640_block * b3 ,
struct morus640_block * b4 ,
const struct morus640_block * m ,
unsigned int b , unsigned int w )
{
unsigned int i ;
struct morus640_block tmp ;
for ( i = 0 ; i < MORUS_BLOCK_WORDS ; i + + ) {
b0 - > words [ i ] ^ = b1 - > words [ i ] & b2 - > words [ i ] ;
b0 - > words [ i ] ^ = b3 - > words [ i ] ;
b0 - > words [ i ] ^ = m - > words [ i ] ;
b0 - > words [ i ] = rol32 ( b0 - > words [ i ] , b ) ;
}
tmp = * b3 ;
for ( i = 0 ; i < MORUS_BLOCK_WORDS ; i + + )
b3 - > words [ ( i + w ) % MORUS_BLOCK_WORDS ] = tmp . words [ i ] ;
}
static void crypto_morus640_update ( struct morus640_state * state ,
const struct morus640_block * m )
{
static const struct morus640_block z = { } ;
struct morus640_block * s = state - > s ;
crypto_morus640_round ( & s [ 0 ] , & s [ 1 ] , & s [ 2 ] , & s [ 3 ] , & s [ 4 ] , & z , 5 , 1 ) ;
crypto_morus640_round ( & s [ 1 ] , & s [ 2 ] , & s [ 3 ] , & s [ 4 ] , & s [ 0 ] , m , 31 , 2 ) ;
crypto_morus640_round ( & s [ 2 ] , & s [ 3 ] , & s [ 4 ] , & s [ 0 ] , & s [ 1 ] , m , 7 , 3 ) ;
crypto_morus640_round ( & s [ 3 ] , & s [ 4 ] , & s [ 0 ] , & s [ 1 ] , & s [ 2 ] , m , 22 , 2 ) ;
crypto_morus640_round ( & s [ 4 ] , & s [ 0 ] , & s [ 1 ] , & s [ 2 ] , & s [ 3 ] , m , 13 , 1 ) ;
}
static void crypto_morus640_load_a ( struct morus640_block * dst , const u8 * src )
{
unsigned int i ;
for ( i = 0 ; i < MORUS_BLOCK_WORDS ; i + + ) {
dst - > words [ i ] = le32_to_cpu ( * ( const __le32 * ) src ) ;
src + = MORUS640_WORD_SIZE ;
}
}
static void crypto_morus640_load_u ( struct morus640_block * dst , const u8 * src )
{
unsigned int i ;
for ( i = 0 ; i < MORUS_BLOCK_WORDS ; i + + ) {
dst - > words [ i ] = get_unaligned_le32 ( src ) ;
src + = MORUS640_WORD_SIZE ;
}
}
static void crypto_morus640_load ( struct morus640_block * dst , const u8 * src )
{
if ( MORUS640_ALIGNED ( src ) )
crypto_morus640_load_a ( dst , src ) ;
else
crypto_morus640_load_u ( dst , src ) ;
}
static void crypto_morus640_store_a ( u8 * dst , const struct morus640_block * src )
{
unsigned int i ;
for ( i = 0 ; i < MORUS_BLOCK_WORDS ; i + + ) {
* ( __le32 * ) dst = cpu_to_le32 ( src - > words [ i ] ) ;
dst + = MORUS640_WORD_SIZE ;
}
}
static void crypto_morus640_store_u ( u8 * dst , const struct morus640_block * src )
{
unsigned int i ;
for ( i = 0 ; i < MORUS_BLOCK_WORDS ; i + + ) {
put_unaligned_le32 ( src - > words [ i ] , dst ) ;
dst + = MORUS640_WORD_SIZE ;
}
}
static void crypto_morus640_store ( u8 * dst , const struct morus640_block * src )
{
if ( MORUS640_ALIGNED ( dst ) )
crypto_morus640_store_a ( dst , src ) ;
else
crypto_morus640_store_u ( dst , src ) ;
}
static void crypto_morus640_ad ( struct morus640_state * state , const u8 * src ,
unsigned int size )
{
struct morus640_block m ;
if ( MORUS640_ALIGNED ( src ) ) {
while ( size > = MORUS640_BLOCK_SIZE ) {
crypto_morus640_load_a ( & m , src ) ;
crypto_morus640_update ( state , & m ) ;
size - = MORUS640_BLOCK_SIZE ;
src + = MORUS640_BLOCK_SIZE ;
}
} else {
while ( size > = MORUS640_BLOCK_SIZE ) {
crypto_morus640_load_u ( & m , src ) ;
crypto_morus640_update ( state , & m ) ;
size - = MORUS640_BLOCK_SIZE ;
src + = MORUS640_BLOCK_SIZE ;
}
}
}
static void crypto_morus640_core ( const struct morus640_state * state ,
struct morus640_block * blk )
{
unsigned int i ;
for ( i = 0 ; i < MORUS_BLOCK_WORDS ; i + + )
blk - > words [ ( i + 3 ) % MORUS_BLOCK_WORDS ] ^ = state - > s [ 1 ] . words [ i ] ;
for ( i = 0 ; i < MORUS_BLOCK_WORDS ; i + + ) {
blk - > words [ i ] ^ = state - > s [ 0 ] . words [ i ] ;
blk - > words [ i ] ^ = state - > s [ 2 ] . words [ i ] & state - > s [ 3 ] . words [ i ] ;
}
}
static void crypto_morus640_encrypt_chunk ( struct morus640_state * state , u8 * dst ,
const u8 * src , unsigned int size )
{
struct morus640_block c , m ;
if ( MORUS640_ALIGNED ( src ) & & MORUS640_ALIGNED ( dst ) ) {
while ( size > = MORUS640_BLOCK_SIZE ) {
crypto_morus640_load_a ( & m , src ) ;
c = m ;
crypto_morus640_core ( state , & c ) ;
crypto_morus640_store_a ( dst , & c ) ;
crypto_morus640_update ( state , & m ) ;
src + = MORUS640_BLOCK_SIZE ;
dst + = MORUS640_BLOCK_SIZE ;
size - = MORUS640_BLOCK_SIZE ;
}
} else {
while ( size > = MORUS640_BLOCK_SIZE ) {
crypto_morus640_load_u ( & m , src ) ;
c = m ;
crypto_morus640_core ( state , & c ) ;
crypto_morus640_store_u ( dst , & c ) ;
crypto_morus640_update ( state , & m ) ;
src + = MORUS640_BLOCK_SIZE ;
dst + = MORUS640_BLOCK_SIZE ;
size - = MORUS640_BLOCK_SIZE ;
}
}
if ( size > 0 ) {
union morus640_block_in tail ;
memcpy ( tail . bytes , src , size ) ;
memset ( tail . bytes + size , 0 , MORUS640_BLOCK_SIZE - size ) ;
crypto_morus640_load_a ( & m , tail . bytes ) ;
c = m ;
crypto_morus640_core ( state , & c ) ;
crypto_morus640_store_a ( tail . bytes , & c ) ;
crypto_morus640_update ( state , & m ) ;
memcpy ( dst , tail . bytes , size ) ;
}
}
static void crypto_morus640_decrypt_chunk ( struct morus640_state * state , u8 * dst ,
const u8 * src , unsigned int size )
{
struct morus640_block m ;
if ( MORUS640_ALIGNED ( src ) & & MORUS640_ALIGNED ( dst ) ) {
while ( size > = MORUS640_BLOCK_SIZE ) {
crypto_morus640_load_a ( & m , src ) ;
crypto_morus640_core ( state , & m ) ;
crypto_morus640_store_a ( dst , & m ) ;
crypto_morus640_update ( state , & m ) ;
src + = MORUS640_BLOCK_SIZE ;
dst + = MORUS640_BLOCK_SIZE ;
size - = MORUS640_BLOCK_SIZE ;
}
} else {
while ( size > = MORUS640_BLOCK_SIZE ) {
crypto_morus640_load_u ( & m , src ) ;
crypto_morus640_core ( state , & m ) ;
crypto_morus640_store_u ( dst , & m ) ;
crypto_morus640_update ( state , & m ) ;
src + = MORUS640_BLOCK_SIZE ;
dst + = MORUS640_BLOCK_SIZE ;
size - = MORUS640_BLOCK_SIZE ;
}
}
if ( size > 0 ) {
union morus640_block_in tail ;
memcpy ( tail . bytes , src , size ) ;
2018-06-13 17:44:17 +03:00
memset ( tail . bytes + size , 0 , MORUS640_BLOCK_SIZE - size ) ;
2018-05-11 15:19:09 +03:00
2018-06-13 17:44:17 +03:00
crypto_morus640_load_a ( & m , tail . bytes ) ;
2018-05-11 15:19:09 +03:00
crypto_morus640_core ( state , & m ) ;
crypto_morus640_store_a ( tail . bytes , & m ) ;
memset ( tail . bytes + size , 0 , MORUS640_BLOCK_SIZE - size ) ;
crypto_morus640_load_a ( & m , tail . bytes ) ;
crypto_morus640_update ( state , & m ) ;
memcpy ( dst , tail . bytes , size ) ;
}
}
static void crypto_morus640_init ( struct morus640_state * state ,
const struct morus640_block * key ,
const u8 * iv )
{
static const struct morus640_block z = { } ;
unsigned int i ;
crypto_morus640_load ( & state - > s [ 0 ] , iv ) ;
state - > s [ 1 ] = * key ;
for ( i = 0 ; i < MORUS_BLOCK_WORDS ; i + + )
state - > s [ 2 ] . words [ i ] = U32_C ( 0xFFFFFFFF ) ;
state - > s [ 3 ] = crypto_morus640_const [ 0 ] ;
state - > s [ 4 ] = crypto_morus640_const [ 1 ] ;
for ( i = 0 ; i < 16 ; i + + )
crypto_morus640_update ( state , & z ) ;
for ( i = 0 ; i < MORUS_BLOCK_WORDS ; i + + )
state - > s [ 1 ] . words [ i ] ^ = key - > words [ i ] ;
}
static void crypto_morus640_process_ad ( struct morus640_state * state ,
struct scatterlist * sg_src ,
unsigned int assoclen )
{
struct scatter_walk walk ;
struct morus640_block m ;
union morus640_block_in buf ;
unsigned int pos = 0 ;
scatterwalk_start ( & walk , sg_src ) ;
while ( assoclen ! = 0 ) {
unsigned int size = scatterwalk_clamp ( & walk , assoclen ) ;
unsigned int left = size ;
void * mapped = scatterwalk_map ( & walk ) ;
const u8 * src = ( const u8 * ) mapped ;
if ( pos + size > = MORUS640_BLOCK_SIZE ) {
if ( pos > 0 ) {
unsigned int fill = MORUS640_BLOCK_SIZE - pos ;
memcpy ( buf . bytes + pos , src , fill ) ;
crypto_morus640_load_a ( & m , buf . bytes ) ;
crypto_morus640_update ( state , & m ) ;
pos = 0 ;
left - = fill ;
src + = fill ;
}
crypto_morus640_ad ( state , src , left ) ;
src + = left & ~ ( MORUS640_BLOCK_SIZE - 1 ) ;
left & = MORUS640_BLOCK_SIZE - 1 ;
}
memcpy ( buf . bytes + pos , src , left ) ;
pos + = left ;
assoclen - = size ;
scatterwalk_unmap ( mapped ) ;
scatterwalk_advance ( & walk , size ) ;
scatterwalk_done ( & walk , 0 , assoclen ) ;
}
if ( pos > 0 ) {
memset ( buf . bytes + pos , 0 , MORUS640_BLOCK_SIZE - pos ) ;
crypto_morus640_load_a ( & m , buf . bytes ) ;
crypto_morus640_update ( state , & m ) ;
}
}
static void crypto_morus640_process_crypt ( struct morus640_state * state ,
struct aead_request * req ,
const struct morus640_ops * ops )
{
struct skcipher_walk walk ;
u8 * dst ;
const u8 * src ;
ops - > skcipher_walk_init ( & walk , req , false ) ;
while ( walk . nbytes ) {
src = walk . src . virt . addr ;
dst = walk . dst . virt . addr ;
ops - > crypt_chunk ( state , dst , src , walk . nbytes ) ;
skcipher_walk_done ( & walk , 0 ) ;
}
}
static void crypto_morus640_final ( struct morus640_state * state ,
struct morus640_block * tag_xor ,
u64 assoclen , u64 cryptlen )
{
struct morus640_block tmp ;
unsigned int i ;
2018-10-01 11:36:37 +03:00
tmp . words [ 0 ] = lower_32_bits ( assoclen * 8 ) ;
tmp . words [ 1 ] = upper_32_bits ( assoclen * 8 ) ;
tmp . words [ 2 ] = lower_32_bits ( cryptlen * 8 ) ;
tmp . words [ 3 ] = upper_32_bits ( cryptlen * 8 ) ;
2018-05-11 15:19:09 +03:00
for ( i = 0 ; i < MORUS_BLOCK_WORDS ; i + + )
state - > s [ 4 ] . words [ i ] ^ = state - > s [ 0 ] . words [ i ] ;
for ( i = 0 ; i < 10 ; i + + )
crypto_morus640_update ( state , & tmp ) ;
crypto_morus640_core ( state , tag_xor ) ;
}
static int crypto_morus640_setkey ( struct crypto_aead * aead , const u8 * key ,
unsigned int keylen )
{
struct morus640_ctx * ctx = crypto_aead_ctx ( aead ) ;
if ( keylen ! = MORUS640_BLOCK_SIZE ) {
crypto_aead_set_flags ( aead , CRYPTO_TFM_RES_BAD_KEY_LEN ) ;
return - EINVAL ;
}
crypto_morus640_load ( & ctx - > key , key ) ;
return 0 ;
}
static int crypto_morus640_setauthsize ( struct crypto_aead * tfm ,
unsigned int authsize )
{
return ( authsize < = MORUS_MAX_AUTH_SIZE ) ? 0 : - EINVAL ;
}
static void crypto_morus640_crypt ( struct aead_request * req ,
struct morus640_block * tag_xor ,
unsigned int cryptlen ,
const struct morus640_ops * ops )
{
struct crypto_aead * tfm = crypto_aead_reqtfm ( req ) ;
struct morus640_ctx * ctx = crypto_aead_ctx ( tfm ) ;
struct morus640_state state ;
crypto_morus640_init ( & state , & ctx - > key , req - > iv ) ;
crypto_morus640_process_ad ( & state , req - > src , req - > assoclen ) ;
crypto_morus640_process_crypt ( & state , req , ops ) ;
crypto_morus640_final ( & state , tag_xor , req - > assoclen , cryptlen ) ;
}
static int crypto_morus640_encrypt ( struct aead_request * req )
{
static const struct morus640_ops ops = {
. skcipher_walk_init = skcipher_walk_aead_encrypt ,
. crypt_chunk = crypto_morus640_encrypt_chunk ,
} ;
struct crypto_aead * tfm = crypto_aead_reqtfm ( req ) ;
struct morus640_block tag = { } ;
union morus640_block_in tag_out ;
unsigned int authsize = crypto_aead_authsize ( tfm ) ;
unsigned int cryptlen = req - > cryptlen ;
crypto_morus640_crypt ( req , & tag , cryptlen , & ops ) ;
crypto_morus640_store ( tag_out . bytes , & tag ) ;
scatterwalk_map_and_copy ( tag_out . bytes , req - > dst ,
req - > assoclen + cryptlen , authsize , 1 ) ;
return 0 ;
}
static int crypto_morus640_decrypt ( struct aead_request * req )
{
static const struct morus640_ops ops = {
. skcipher_walk_init = skcipher_walk_aead_decrypt ,
. crypt_chunk = crypto_morus640_decrypt_chunk ,
} ;
static const u8 zeros [ MORUS640_BLOCK_SIZE ] = { } ;
struct crypto_aead * tfm = crypto_aead_reqtfm ( req ) ;
union morus640_block_in tag_in ;
struct morus640_block tag ;
unsigned int authsize = crypto_aead_authsize ( tfm ) ;
unsigned int cryptlen = req - > cryptlen - authsize ;
scatterwalk_map_and_copy ( tag_in . bytes , req - > src ,
req - > assoclen + cryptlen , authsize , 0 ) ;
crypto_morus640_load ( & tag , tag_in . bytes ) ;
crypto_morus640_crypt ( req , & tag , cryptlen , & ops ) ;
crypto_morus640_store ( tag_in . bytes , & tag ) ;
return crypto_memneq ( tag_in . bytes , zeros , authsize ) ? - EBADMSG : 0 ;
}
static int crypto_morus640_init_tfm ( struct crypto_aead * tfm )
{
return 0 ;
}
static void crypto_morus640_exit_tfm ( struct crypto_aead * tfm )
{
}
static struct aead_alg crypto_morus640_alg = {
. setkey = crypto_morus640_setkey ,
. setauthsize = crypto_morus640_setauthsize ,
. encrypt = crypto_morus640_encrypt ,
. decrypt = crypto_morus640_decrypt ,
. init = crypto_morus640_init_tfm ,
. exit = crypto_morus640_exit_tfm ,
. ivsize = MORUS_NONCE_SIZE ,
. maxauthsize = MORUS_MAX_AUTH_SIZE ,
. chunksize = MORUS640_BLOCK_SIZE ,
. base = {
. cra_blocksize = 1 ,
. cra_ctxsize = sizeof ( struct morus640_ctx ) ,
. cra_alignmask = 0 ,
. cra_priority = 100 ,
. cra_name = " morus640 " ,
. cra_driver_name = " morus640-generic " ,
. cra_module = THIS_MODULE ,
}
} ;
static int __init crypto_morus640_module_init ( void )
{
return crypto_register_aead ( & crypto_morus640_alg ) ;
}
static void __exit crypto_morus640_module_exit ( void )
{
crypto_unregister_aead ( & crypto_morus640_alg ) ;
}
module_init ( crypto_morus640_module_init ) ;
module_exit ( crypto_morus640_module_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Ondrej Mosnacek <omosnacek@gmail.com> " ) ;
MODULE_DESCRIPTION ( " MORUS-640 AEAD algorithm " ) ;
MODULE_ALIAS_CRYPTO ( " morus640 " ) ;
MODULE_ALIAS_CRYPTO ( " morus640-generic " ) ;