2015-06-01 14:43:56 +03:00
/*
* ChaCha20 256 - bit cipher algorithm , RFC7539
*
* Copyright ( C ) 2015 Martin Willi
*
* 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 .
*/
2017-11-22 22:51:36 +03:00
# include <asm/unaligned.h>
2015-06-01 14:43:56 +03:00
# include <crypto/algapi.h>
2015-07-16 20:14:00 +03:00
# include <crypto/chacha20.h>
2016-12-09 17:33:51 +03:00
# include <crypto/internal/skcipher.h>
# include <linux/module.h>
2015-06-01 14:43:56 +03:00
static void chacha20_docrypt ( u32 * state , u8 * dst , const u8 * src ,
unsigned int bytes )
{
2017-11-22 22:51:39 +03:00
u32 stream [ CHACHA20_BLOCK_WORDS ] ;
2015-06-01 14:43:56 +03:00
if ( dst ! = src )
memcpy ( dst , src , bytes ) ;
while ( bytes > = CHACHA20_BLOCK_SIZE ) {
chacha20_block ( state , stream ) ;
2017-11-22 22:51:39 +03:00
crypto_xor ( dst , ( const u8 * ) stream , CHACHA20_BLOCK_SIZE ) ;
2015-06-01 14:43:56 +03:00
bytes - = CHACHA20_BLOCK_SIZE ;
dst + = CHACHA20_BLOCK_SIZE ;
}
if ( bytes ) {
chacha20_block ( state , stream ) ;
2017-11-22 22:51:39 +03:00
crypto_xor ( dst , ( const u8 * ) stream , bytes ) ;
2015-06-01 14:43:56 +03:00
}
}
2015-07-16 20:14:00 +03:00
void crypto_chacha20_init ( u32 * state , struct chacha20_ctx * ctx , u8 * iv )
2015-06-01 14:43:56 +03:00
{
2017-11-22 22:51:35 +03:00
state [ 0 ] = 0x61707865 ; /* "expa" */
state [ 1 ] = 0x3320646e ; /* "nd 3" */
state [ 2 ] = 0x79622d32 ; /* "2-by" */
state [ 3 ] = 0x6b206574 ; /* "te k" */
2015-06-01 14:43:56 +03:00
state [ 4 ] = ctx - > key [ 0 ] ;
state [ 5 ] = ctx - > key [ 1 ] ;
state [ 6 ] = ctx - > key [ 2 ] ;
state [ 7 ] = ctx - > key [ 3 ] ;
state [ 8 ] = ctx - > key [ 4 ] ;
state [ 9 ] = ctx - > key [ 5 ] ;
state [ 10 ] = ctx - > key [ 6 ] ;
state [ 11 ] = ctx - > key [ 7 ] ;
2017-11-22 22:51:36 +03:00
state [ 12 ] = get_unaligned_le32 ( iv + 0 ) ;
state [ 13 ] = get_unaligned_le32 ( iv + 4 ) ;
state [ 14 ] = get_unaligned_le32 ( iv + 8 ) ;
state [ 15 ] = get_unaligned_le32 ( iv + 12 ) ;
2015-06-01 14:43:56 +03:00
}
2015-07-16 20:14:00 +03:00
EXPORT_SYMBOL_GPL ( crypto_chacha20_init ) ;
2015-06-01 14:43:56 +03:00
2016-12-09 17:33:51 +03:00
int crypto_chacha20_setkey ( struct crypto_skcipher * tfm , const u8 * key ,
2015-06-01 14:43:56 +03:00
unsigned int keysize )
{
2016-12-09 17:33:51 +03:00
struct chacha20_ctx * ctx = crypto_skcipher_ctx ( tfm ) ;
2015-06-01 14:43:56 +03:00
int i ;
if ( keysize ! = CHACHA20_KEY_SIZE )
return - EINVAL ;
for ( i = 0 ; i < ARRAY_SIZE ( ctx - > key ) ; i + + )
2017-11-22 22:51:36 +03:00
ctx - > key [ i ] = get_unaligned_le32 ( key + i * sizeof ( u32 ) ) ;
2015-06-01 14:43:56 +03:00
return 0 ;
}
2015-07-16 20:14:00 +03:00
EXPORT_SYMBOL_GPL ( crypto_chacha20_setkey ) ;
2015-06-01 14:43:56 +03:00
2016-12-09 17:33:51 +03:00
int crypto_chacha20_crypt ( struct skcipher_request * req )
2015-06-01 14:43:56 +03:00
{
2016-12-09 17:33:51 +03:00
struct crypto_skcipher * tfm = crypto_skcipher_reqtfm ( req ) ;
struct chacha20_ctx * ctx = crypto_skcipher_ctx ( tfm ) ;
struct skcipher_walk walk ;
2015-06-01 14:43:56 +03:00
u32 state [ 16 ] ;
int err ;
2016-12-09 17:33:51 +03:00
err = skcipher_walk_virt ( & walk , req , true ) ;
2015-06-01 14:43:56 +03:00
2016-12-09 17:33:51 +03:00
crypto_chacha20_init ( state , ctx , walk . iv ) ;
2015-06-01 14:43:56 +03:00
2016-12-09 17:33:51 +03:00
while ( walk . nbytes > 0 ) {
2017-08-14 16:28:14 +03:00
unsigned int nbytes = walk . nbytes ;
if ( nbytes < walk . total )
nbytes = round_down ( nbytes , walk . stride ) ;
2015-06-01 14:43:56 +03:00
chacha20_docrypt ( state , walk . dst . virt . addr , walk . src . virt . addr ,
2017-08-14 16:28:14 +03:00
nbytes ) ;
err = skcipher_walk_done ( & walk , walk . nbytes - nbytes ) ;
2015-06-01 14:43:56 +03:00
}
return err ;
}
2015-07-16 20:14:00 +03:00
EXPORT_SYMBOL_GPL ( crypto_chacha20_crypt ) ;
2015-06-01 14:43:56 +03:00
2016-12-09 17:33:51 +03:00
static struct skcipher_alg alg = {
. base . cra_name = " chacha20 " ,
. base . cra_driver_name = " chacha20-generic " ,
. base . cra_priority = 100 ,
. base . cra_blocksize = 1 ,
. base . cra_ctxsize = sizeof ( struct chacha20_ctx ) ,
. base . cra_module = THIS_MODULE ,
. min_keysize = CHACHA20_KEY_SIZE ,
. max_keysize = CHACHA20_KEY_SIZE ,
. ivsize = CHACHA20_IV_SIZE ,
. chunksize = CHACHA20_BLOCK_SIZE ,
. setkey = crypto_chacha20_setkey ,
. encrypt = crypto_chacha20_crypt ,
. decrypt = crypto_chacha20_crypt ,
2015-06-01 14:43:56 +03:00
} ;
static int __init chacha20_generic_mod_init ( void )
{
2016-12-09 17:33:51 +03:00
return crypto_register_skcipher ( & alg ) ;
2015-06-01 14:43:56 +03:00
}
static void __exit chacha20_generic_mod_fini ( void )
{
2016-12-09 17:33:51 +03:00
crypto_unregister_skcipher ( & alg ) ;
2015-06-01 14:43:56 +03:00
}
module_init ( chacha20_generic_mod_init ) ;
module_exit ( chacha20_generic_mod_fini ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Martin Willi <martin@strongswan.org> " ) ;
MODULE_DESCRIPTION ( " chacha20 cipher algorithm " ) ;
MODULE_ALIAS_CRYPTO ( " chacha20 " ) ;
MODULE_ALIAS_CRYPTO ( " chacha20-generic " ) ;