2012-07-11 21:37:37 +04:00
/*
* Glue Code for the AVX assembler implemention of the Cast5 Cipher
*
* Copyright ( C ) 2012 Johannes Goetzfried
* < Johannes . Goetzfried @ informatik . stud . uni - erlangen . de >
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
* USA
*
*/
2018-02-20 10:48:13 +03:00
# include <asm/crypto/glue_helper.h>
2012-07-11 21:37:37 +04:00
# include <crypto/algapi.h>
# include <crypto/cast5.h>
2018-02-20 10:48:13 +03:00
# include <crypto/internal/simd.h>
# include <linux/crypto.h>
# include <linux/err.h>
# include <linux/module.h>
# include <linux/types.h>
2012-07-11 21:37:37 +04:00
# define CAST5_PARALLEL_BLOCKS 16
2012-10-20 16:06:56 +04:00
asmlinkage void cast5_ecb_enc_16way ( struct cast5_ctx * ctx , u8 * dst ,
2012-07-11 21:37:37 +04:00
const u8 * src ) ;
2012-10-20 16:06:56 +04:00
asmlinkage void cast5_ecb_dec_16way ( struct cast5_ctx * ctx , u8 * dst ,
const u8 * src ) ;
asmlinkage void cast5_cbc_dec_16way ( struct cast5_ctx * ctx , u8 * dst ,
const u8 * src ) ;
asmlinkage void cast5_ctr_16way ( struct cast5_ctx * ctx , u8 * dst , const u8 * src ,
__be64 * iv ) ;
2012-07-11 21:37:37 +04:00
2018-02-20 10:48:13 +03:00
static int cast5_setkey_skcipher ( struct crypto_skcipher * tfm , const u8 * key ,
unsigned int keylen )
2012-07-11 21:37:37 +04:00
{
2018-02-20 10:48:13 +03:00
return cast5_setkey ( & tfm - > base , key , keylen ) ;
}
static inline bool cast5_fpu_begin ( bool fpu_enabled , struct skcipher_walk * walk ,
unsigned int nbytes )
{
2018-02-20 10:48:27 +03:00
return glue_fpu_begin ( CAST5_BLOCK_SIZE , CAST5_PARALLEL_BLOCKS ,
walk , fpu_enabled , nbytes ) ;
2012-07-11 21:37:37 +04:00
}
static inline void cast5_fpu_end ( bool fpu_enabled )
{
return glue_fpu_end ( fpu_enabled ) ;
}
2018-02-20 10:48:13 +03:00
static int ecb_crypt ( struct skcipher_request * req , bool enc )
2012-07-11 21:37:37 +04:00
{
bool fpu_enabled = false ;
2018-02-20 10:48:13 +03:00
struct crypto_skcipher * tfm = crypto_skcipher_reqtfm ( req ) ;
struct cast5_ctx * ctx = crypto_skcipher_ctx ( tfm ) ;
struct skcipher_walk walk ;
2012-07-11 21:37:37 +04:00
const unsigned int bsize = CAST5_BLOCK_SIZE ;
unsigned int nbytes ;
2012-10-20 16:06:56 +04:00
void ( * fn ) ( struct cast5_ctx * ctx , u8 * dst , const u8 * src ) ;
2012-07-11 21:37:37 +04:00
int err ;
2018-02-20 10:48:13 +03:00
err = skcipher_walk_virt ( & walk , req , false ) ;
2012-07-11 21:37:37 +04:00
2018-02-20 10:48:13 +03:00
while ( ( nbytes = walk . nbytes ) ) {
u8 * wsrc = walk . src . virt . addr ;
u8 * wdst = walk . dst . virt . addr ;
2012-07-11 21:37:37 +04:00
2018-02-20 10:48:13 +03:00
fpu_enabled = cast5_fpu_begin ( fpu_enabled , & walk , nbytes ) ;
2012-07-11 21:37:37 +04:00
/* Process multi-block batch */
if ( nbytes > = bsize * CAST5_PARALLEL_BLOCKS ) {
2018-02-20 10:48:12 +03:00
fn = ( enc ) ? cast5_ecb_enc_16way : cast5_ecb_dec_16way ;
2012-07-11 21:37:37 +04:00
do {
2012-10-20 16:06:56 +04:00
fn ( ctx , wdst , wsrc ) ;
2012-07-11 21:37:37 +04:00
wsrc + = bsize * CAST5_PARALLEL_BLOCKS ;
wdst + = bsize * CAST5_PARALLEL_BLOCKS ;
nbytes - = bsize * CAST5_PARALLEL_BLOCKS ;
} while ( nbytes > = bsize * CAST5_PARALLEL_BLOCKS ) ;
if ( nbytes < bsize )
goto done ;
}
2012-10-20 16:06:56 +04:00
fn = ( enc ) ? __cast5_encrypt : __cast5_decrypt ;
2012-07-11 21:37:37 +04:00
/* Handle leftovers */
do {
2012-10-20 16:06:56 +04:00
fn ( ctx , wdst , wsrc ) ;
2012-07-11 21:37:37 +04:00
wsrc + = bsize ;
wdst + = bsize ;
nbytes - = bsize ;
} while ( nbytes > = bsize ) ;
done :
2018-02-20 10:48:13 +03:00
err = skcipher_walk_done ( & walk , nbytes ) ;
2012-07-11 21:37:37 +04:00
}
cast5_fpu_end ( fpu_enabled ) ;
return err ;
}
2018-02-20 10:48:13 +03:00
static int ecb_encrypt ( struct skcipher_request * req )
2012-07-11 21:37:37 +04:00
{
2018-02-20 10:48:13 +03:00
return ecb_crypt ( req , true ) ;
2012-07-11 21:37:37 +04:00
}
2018-02-20 10:48:13 +03:00
static int ecb_decrypt ( struct skcipher_request * req )
2012-07-11 21:37:37 +04:00
{
2018-02-20 10:48:13 +03:00
return ecb_crypt ( req , false ) ;
2012-07-11 21:37:37 +04:00
}
2018-02-20 10:48:13 +03:00
static int cbc_encrypt ( struct skcipher_request * req )
2012-07-11 21:37:37 +04:00
{
const unsigned int bsize = CAST5_BLOCK_SIZE ;
2018-02-20 10:48:13 +03:00
struct crypto_skcipher * tfm = crypto_skcipher_reqtfm ( req ) ;
struct cast5_ctx * ctx = crypto_skcipher_ctx ( tfm ) ;
struct skcipher_walk walk ;
unsigned int nbytes ;
2012-07-11 21:37:37 +04:00
int err ;
2018-02-20 10:48:13 +03:00
err = skcipher_walk_virt ( & walk , req , false ) ;
2012-07-11 21:37:37 +04:00
while ( ( nbytes = walk . nbytes ) ) {
2018-02-20 10:48:13 +03:00
u64 * src = ( u64 * ) walk . src . virt . addr ;
u64 * dst = ( u64 * ) walk . dst . virt . addr ;
u64 * iv = ( u64 * ) walk . iv ;
do {
* dst = * src ^ * iv ;
__cast5_encrypt ( ctx , ( u8 * ) dst , ( u8 * ) dst ) ;
iv = dst ;
src + + ;
dst + + ;
nbytes - = bsize ;
} while ( nbytes > = bsize ) ;
* ( u64 * ) walk . iv = * iv ;
err = skcipher_walk_done ( & walk , nbytes ) ;
2012-07-11 21:37:37 +04:00
}
return err ;
}
2018-02-20 10:48:13 +03:00
static unsigned int __cbc_decrypt ( struct cast5_ctx * ctx ,
struct skcipher_walk * walk )
2012-07-11 21:37:37 +04:00
{
const unsigned int bsize = CAST5_BLOCK_SIZE ;
unsigned int nbytes = walk - > nbytes ;
u64 * src = ( u64 * ) walk - > src . virt . addr ;
u64 * dst = ( u64 * ) walk - > dst . virt . addr ;
u64 last_iv ;
/* Start of the last block. */
src + = nbytes / bsize - 1 ;
dst + = nbytes / bsize - 1 ;
last_iv = * src ;
/* Process multi-block batch */
if ( nbytes > = bsize * CAST5_PARALLEL_BLOCKS ) {
do {
nbytes - = bsize * ( CAST5_PARALLEL_BLOCKS - 1 ) ;
src - = CAST5_PARALLEL_BLOCKS - 1 ;
dst - = CAST5_PARALLEL_BLOCKS - 1 ;
2012-10-20 16:06:56 +04:00
cast5_cbc_dec_16way ( ctx , ( u8 * ) dst , ( u8 * ) src ) ;
2012-07-11 21:37:37 +04:00
nbytes - = bsize ;
if ( nbytes < bsize )
goto done ;
* dst ^ = * ( src - 1 ) ;
src - = 1 ;
dst - = 1 ;
} while ( nbytes > = bsize * CAST5_PARALLEL_BLOCKS ) ;
}
/* Handle leftovers */
for ( ; ; ) {
__cast5_decrypt ( ctx , ( u8 * ) dst , ( u8 * ) src ) ;
nbytes - = bsize ;
if ( nbytes < bsize )
break ;
* dst ^ = * ( src - 1 ) ;
src - = 1 ;
dst - = 1 ;
}
done :
* dst ^ = * ( u64 * ) walk - > iv ;
* ( u64 * ) walk - > iv = last_iv ;
return nbytes ;
}
2018-02-20 10:48:13 +03:00
static int cbc_decrypt ( struct skcipher_request * req )
2012-07-11 21:37:37 +04:00
{
2018-02-20 10:48:13 +03:00
struct crypto_skcipher * tfm = crypto_skcipher_reqtfm ( req ) ;
struct cast5_ctx * ctx = crypto_skcipher_ctx ( tfm ) ;
2012-07-11 21:37:37 +04:00
bool fpu_enabled = false ;
2018-02-20 10:48:13 +03:00
struct skcipher_walk walk ;
unsigned int nbytes ;
2012-07-11 21:37:37 +04:00
int err ;
2018-02-20 10:48:13 +03:00
err = skcipher_walk_virt ( & walk , req , false ) ;
2012-07-11 21:37:37 +04:00
while ( ( nbytes = walk . nbytes ) ) {
2018-02-20 10:48:13 +03:00
fpu_enabled = cast5_fpu_begin ( fpu_enabled , & walk , nbytes ) ;
nbytes = __cbc_decrypt ( ctx , & walk ) ;
err = skcipher_walk_done ( & walk , nbytes ) ;
2012-07-11 21:37:37 +04:00
}
cast5_fpu_end ( fpu_enabled ) ;
return err ;
}
2018-02-20 10:48:13 +03:00
static void ctr_crypt_final ( struct skcipher_walk * walk , struct cast5_ctx * ctx )
2012-07-11 21:37:37 +04:00
{
u8 * ctrblk = walk - > iv ;
u8 keystream [ CAST5_BLOCK_SIZE ] ;
u8 * src = walk - > src . virt . addr ;
u8 * dst = walk - > dst . virt . addr ;
unsigned int nbytes = walk - > nbytes ;
__cast5_encrypt ( ctx , keystream , ctrblk ) ;
crypto: algapi - make crypto_xor() take separate dst and src arguments
There are quite a number of occurrences in the kernel of the pattern
if (dst != src)
memcpy(dst, src, walk.total % AES_BLOCK_SIZE);
crypto_xor(dst, final, walk.total % AES_BLOCK_SIZE);
or
crypto_xor(keystream, src, nbytes);
memcpy(dst, keystream, nbytes);
where crypto_xor() is preceded or followed by a memcpy() invocation
that is only there because crypto_xor() uses its output parameter as
one of the inputs. To avoid having to add new instances of this pattern
in the arm64 code, which will be refactored to implement non-SIMD
fallbacks, add an alternative implementation called crypto_xor_cpy(),
taking separate input and output arguments. This removes the need for
the separate memcpy().
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2017-07-24 13:28:04 +03:00
crypto_xor_cpy ( dst , keystream , src , nbytes ) ;
2012-07-11 21:37:37 +04:00
crypto_inc ( ctrblk , CAST5_BLOCK_SIZE ) ;
}
2018-02-20 10:48:13 +03:00
static unsigned int __ctr_crypt ( struct skcipher_walk * walk ,
struct cast5_ctx * ctx )
2012-07-11 21:37:37 +04:00
{
const unsigned int bsize = CAST5_BLOCK_SIZE ;
unsigned int nbytes = walk - > nbytes ;
u64 * src = ( u64 * ) walk - > src . virt . addr ;
u64 * dst = ( u64 * ) walk - > dst . virt . addr ;
/* Process multi-block batch */
if ( nbytes > = bsize * CAST5_PARALLEL_BLOCKS ) {
do {
2012-10-20 16:06:56 +04:00
cast5_ctr_16way ( ctx , ( u8 * ) dst , ( u8 * ) src ,
( __be64 * ) walk - > iv ) ;
2012-07-11 21:37:37 +04:00
src + = CAST5_PARALLEL_BLOCKS ;
dst + = CAST5_PARALLEL_BLOCKS ;
nbytes - = bsize * CAST5_PARALLEL_BLOCKS ;
} while ( nbytes > = bsize * CAST5_PARALLEL_BLOCKS ) ;
if ( nbytes < bsize )
goto done ;
}
/* Handle leftovers */
do {
2012-10-20 16:06:56 +04:00
u64 ctrblk ;
2012-07-11 21:37:37 +04:00
if ( dst ! = src )
* dst = * src ;
2012-10-20 16:06:56 +04:00
ctrblk = * ( u64 * ) walk - > iv ;
be64_add_cpu ( ( __be64 * ) walk - > iv , 1 ) ;
2012-07-11 21:37:37 +04:00
2012-10-20 16:06:56 +04:00
__cast5_encrypt ( ctx , ( u8 * ) & ctrblk , ( u8 * ) & ctrblk ) ;
* dst ^ = ctrblk ;
2012-07-11 21:37:37 +04:00
src + = 1 ;
dst + = 1 ;
nbytes - = bsize ;
} while ( nbytes > = bsize ) ;
done :
return nbytes ;
}
2018-02-20 10:48:13 +03:00
static int ctr_crypt ( struct skcipher_request * req )
2012-07-11 21:37:37 +04:00
{
2018-02-20 10:48:13 +03:00
struct crypto_skcipher * tfm = crypto_skcipher_reqtfm ( req ) ;
struct cast5_ctx * ctx = crypto_skcipher_ctx ( tfm ) ;
2012-07-11 21:37:37 +04:00
bool fpu_enabled = false ;
2018-02-20 10:48:13 +03:00
struct skcipher_walk walk ;
unsigned int nbytes ;
2012-07-11 21:37:37 +04:00
int err ;
2018-02-20 10:48:13 +03:00
err = skcipher_walk_virt ( & walk , req , false ) ;
2012-07-11 21:37:37 +04:00
while ( ( nbytes = walk . nbytes ) > = CAST5_BLOCK_SIZE ) {
2018-02-20 10:48:13 +03:00
fpu_enabled = cast5_fpu_begin ( fpu_enabled , & walk , nbytes ) ;
nbytes = __ctr_crypt ( & walk , ctx ) ;
err = skcipher_walk_done ( & walk , nbytes ) ;
2012-07-11 21:37:37 +04:00
}
cast5_fpu_end ( fpu_enabled ) ;
if ( walk . nbytes ) {
2018-02-20 10:48:13 +03:00
ctr_crypt_final ( & walk , ctx ) ;
err = skcipher_walk_done ( & walk , 0 ) ;
2012-07-11 21:37:37 +04:00
}
return err ;
}
2018-02-20 10:48:13 +03:00
static struct skcipher_alg cast5_algs [ ] = {
{
. base . cra_name = " __ecb(cast5) " ,
. base . cra_driver_name = " __ecb-cast5-avx " ,
. base . cra_priority = 200 ,
. base . cra_flags = CRYPTO_ALG_INTERNAL ,
. base . cra_blocksize = CAST5_BLOCK_SIZE ,
. base . cra_ctxsize = sizeof ( struct cast5_ctx ) ,
. base . cra_module = THIS_MODULE ,
. min_keysize = CAST5_MIN_KEY_SIZE ,
. max_keysize = CAST5_MAX_KEY_SIZE ,
. setkey = cast5_setkey_skcipher ,
. encrypt = ecb_encrypt ,
. decrypt = ecb_decrypt ,
} , {
. base . cra_name = " __cbc(cast5) " ,
. base . cra_driver_name = " __cbc-cast5-avx " ,
. base . cra_priority = 200 ,
. base . cra_flags = CRYPTO_ALG_INTERNAL ,
. base . cra_blocksize = CAST5_BLOCK_SIZE ,
. base . cra_ctxsize = sizeof ( struct cast5_ctx ) ,
. base . cra_module = THIS_MODULE ,
. min_keysize = CAST5_MIN_KEY_SIZE ,
. max_keysize = CAST5_MAX_KEY_SIZE ,
. ivsize = CAST5_BLOCK_SIZE ,
. setkey = cast5_setkey_skcipher ,
. encrypt = cbc_encrypt ,
. decrypt = cbc_decrypt ,
} , {
. base . cra_name = " __ctr(cast5) " ,
. base . cra_driver_name = " __ctr-cast5-avx " ,
. base . cra_priority = 200 ,
. base . cra_flags = CRYPTO_ALG_INTERNAL ,
. base . cra_blocksize = 1 ,
. base . cra_ctxsize = sizeof ( struct cast5_ctx ) ,
. base . cra_module = THIS_MODULE ,
. min_keysize = CAST5_MIN_KEY_SIZE ,
. max_keysize = CAST5_MAX_KEY_SIZE ,
. ivsize = CAST5_BLOCK_SIZE ,
. chunksize = CAST5_BLOCK_SIZE ,
. setkey = cast5_setkey_skcipher ,
. encrypt = ctr_crypt ,
. decrypt = ctr_crypt ,
}
} ;
2012-07-11 21:37:37 +04:00
2018-02-20 10:48:13 +03:00
static struct simd_skcipher_alg * cast5_simd_algs [ ARRAY_SIZE ( cast5_algs ) ] ;
2012-07-11 21:37:37 +04:00
static int __init cast5_init ( void )
{
2015-04-28 11:11:24 +03:00
const char * feature_name ;
2012-07-11 21:37:37 +04:00
2015-09-03 02:31:26 +03:00
if ( ! cpu_has_xfeatures ( XFEATURE_MASK_SSE | XFEATURE_MASK_YMM ,
& feature_name ) ) {
2015-04-28 11:11:24 +03:00
pr_info ( " CPU feature '%s' is not supported. \n " , feature_name ) ;
2012-07-11 21:37:37 +04:00
return - ENODEV ;
}
2018-02-20 10:48:13 +03:00
return simd_register_skciphers_compat ( cast5_algs ,
ARRAY_SIZE ( cast5_algs ) ,
cast5_simd_algs ) ;
2012-07-11 21:37:37 +04:00
}
static void __exit cast5_exit ( void )
{
2018-02-20 10:48:13 +03:00
simd_unregister_skciphers ( cast5_algs , ARRAY_SIZE ( cast5_algs ) ,
cast5_simd_algs ) ;
2012-07-11 21:37:37 +04:00
}
module_init ( cast5_init ) ;
module_exit ( cast5_exit ) ;
MODULE_DESCRIPTION ( " Cast5 Cipher Algorithm, AVX optimized " ) ;
MODULE_LICENSE ( " GPL " ) ;
2014-11-21 04:05:53 +03:00
MODULE_ALIAS_CRYPTO ( " cast5 " ) ;