2013-11-12 11:46:40 -06:00
/*
* AMD Cryptographic Coprocessor ( CCP ) AES XTS crypto API support
*
* Copyright ( C ) 2013 Advanced Micro Devices , Inc .
*
* Author : Tom Lendacky < thomas . lendacky @ amd . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/delay.h>
# include <linux/scatterlist.h>
# include <linux/crypto.h>
# include <crypto/algapi.h>
# include <crypto/aes.h>
# include <crypto/scatterwalk.h>
# include "ccp-crypto.h"
struct ccp_aes_xts_def {
const char * name ;
const char * drv_name ;
} ;
static struct ccp_aes_xts_def aes_xts_algs [ ] = {
{
. name = " xts(aes) " ,
. drv_name = " xts-aes-ccp " ,
} ,
} ;
struct ccp_unit_size_map {
unsigned int size ;
u32 value ;
} ;
static struct ccp_unit_size_map unit_size_map [ ] = {
{
. size = 4096 ,
. value = CCP_XTS_AES_UNIT_SIZE_4096 ,
} ,
{
. size = 2048 ,
. value = CCP_XTS_AES_UNIT_SIZE_2048 ,
} ,
{
. size = 1024 ,
. value = CCP_XTS_AES_UNIT_SIZE_1024 ,
} ,
{
. size = 512 ,
. value = CCP_XTS_AES_UNIT_SIZE_512 ,
} ,
{
. size = 256 ,
. value = CCP_XTS_AES_UNIT_SIZE__LAST ,
} ,
{
. size = 128 ,
. value = CCP_XTS_AES_UNIT_SIZE__LAST ,
} ,
{
. size = 64 ,
. value = CCP_XTS_AES_UNIT_SIZE__LAST ,
} ,
{
. size = 32 ,
. value = CCP_XTS_AES_UNIT_SIZE__LAST ,
} ,
{
. size = 16 ,
. value = CCP_XTS_AES_UNIT_SIZE_16 ,
} ,
{
. size = 1 ,
. value = CCP_XTS_AES_UNIT_SIZE__LAST ,
} ,
} ;
static int ccp_aes_xts_complete ( struct crypto_async_request * async_req , int ret )
{
struct ablkcipher_request * req = ablkcipher_request_cast ( async_req ) ;
struct ccp_aes_req_ctx * rctx = ablkcipher_request_ctx ( req ) ;
if ( ret )
return ret ;
memcpy ( req - > info , rctx - > iv , AES_BLOCK_SIZE ) ;
return 0 ;
}
static int ccp_aes_xts_setkey ( struct crypto_ablkcipher * tfm , const u8 * key ,
unsigned int key_len )
{
struct ccp_ctx * ctx = crypto_tfm_ctx ( crypto_ablkcipher_tfm ( tfm ) ) ;
/* Only support 128-bit AES key with a 128-bit Tweak key,
* otherwise use the fallback
*/
switch ( key_len ) {
case AES_KEYSIZE_128 * 2 :
memcpy ( ctx - > u . aes . key , key , key_len ) ;
break ;
}
ctx - > u . aes . key_len = key_len / 2 ;
sg_init_one ( & ctx - > u . aes . key_sg , ctx - > u . aes . key , key_len ) ;
return crypto_ablkcipher_setkey ( ctx - > u . aes . tfm_ablkcipher , key ,
key_len ) ;
}
static int ccp_aes_xts_crypt ( struct ablkcipher_request * req ,
unsigned int encrypt )
{
struct crypto_tfm * tfm =
crypto_ablkcipher_tfm ( crypto_ablkcipher_reqtfm ( req ) ) ;
struct ccp_ctx * ctx = crypto_tfm_ctx ( req - > base . tfm ) ;
struct ccp_aes_req_ctx * rctx = ablkcipher_request_ctx ( req ) ;
unsigned int unit ;
2016-05-20 17:33:03 -05:00
u32 unit_size ;
2013-11-12 11:46:40 -06:00
int ret ;
2013-12-10 10:38:44 -06:00
if ( ! ctx - > u . aes . key_len )
2013-11-12 11:46:40 -06:00
return - EINVAL ;
2013-12-10 10:38:44 -06:00
if ( req - > nbytes & ( AES_BLOCK_SIZE - 1 ) )
2013-11-12 11:46:40 -06:00
return - EINVAL ;
2013-12-10 10:38:44 -06:00
if ( ! req - > info )
2013-11-12 11:46:40 -06:00
return - EINVAL ;
2016-05-20 17:33:03 -05:00
unit_size = CCP_XTS_AES_UNIT_SIZE__LAST ;
if ( req - > nbytes < = unit_size_map [ 0 ] . size ) {
for ( unit = 0 ; unit < ARRAY_SIZE ( unit_size_map ) ; unit + + ) {
if ( ! ( req - > nbytes & ( unit_size_map [ unit ] . size - 1 ) ) ) {
unit_size = unit_size_map [ unit ] . value ;
break ;
}
}
}
2013-11-12 11:46:40 -06:00
2016-05-20 17:33:03 -05:00
if ( ( unit_size = = CCP_XTS_AES_UNIT_SIZE__LAST ) | |
2013-11-12 11:46:40 -06:00
( ctx - > u . aes . key_len ! = AES_KEYSIZE_128 ) ) {
/* Use the fallback to process the request for any
* unsupported unit sizes or key sizes
*/
ablkcipher_request_set_tfm ( req , ctx - > u . aes . tfm_ablkcipher ) ;
ret = ( encrypt ) ? crypto_ablkcipher_encrypt ( req ) :
crypto_ablkcipher_decrypt ( req ) ;
ablkcipher_request_set_tfm ( req , __crypto_ablkcipher_cast ( tfm ) ) ;
return ret ;
}
memcpy ( rctx - > iv , req - > info , AES_BLOCK_SIZE ) ;
sg_init_one ( & rctx - > iv_sg , rctx - > iv , AES_BLOCK_SIZE ) ;
memset ( & rctx - > cmd , 0 , sizeof ( rctx - > cmd ) ) ;
INIT_LIST_HEAD ( & rctx - > cmd . entry ) ;
rctx - > cmd . engine = CCP_ENGINE_XTS_AES_128 ;
rctx - > cmd . u . xts . action = ( encrypt ) ? CCP_AES_ACTION_ENCRYPT
: CCP_AES_ACTION_DECRYPT ;
2016-05-20 17:33:03 -05:00
rctx - > cmd . u . xts . unit_size = unit_size ;
2013-11-12 11:46:40 -06:00
rctx - > cmd . u . xts . key = & ctx - > u . aes . key_sg ;
rctx - > cmd . u . xts . key_len = ctx - > u . aes . key_len ;
rctx - > cmd . u . xts . iv = & rctx - > iv_sg ;
rctx - > cmd . u . xts . iv_len = AES_BLOCK_SIZE ;
rctx - > cmd . u . xts . src = req - > src ;
rctx - > cmd . u . xts . src_len = req - > nbytes ;
rctx - > cmd . u . xts . dst = req - > dst ;
ret = ccp_crypto_enqueue_request ( & req - > base , & rctx - > cmd ) ;
return ret ;
}
static int ccp_aes_xts_encrypt ( struct ablkcipher_request * req )
{
return ccp_aes_xts_crypt ( req , 1 ) ;
}
static int ccp_aes_xts_decrypt ( struct ablkcipher_request * req )
{
return ccp_aes_xts_crypt ( req , 0 ) ;
}
static int ccp_aes_xts_cra_init ( struct crypto_tfm * tfm )
{
struct ccp_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
struct crypto_ablkcipher * fallback_tfm ;
ctx - > complete = ccp_aes_xts_complete ;
ctx - > u . aes . key_len = 0 ;
2014-05-14 11:40:58 +02:00
fallback_tfm = crypto_alloc_ablkcipher ( crypto_tfm_alg_name ( tfm ) , 0 ,
2013-11-12 11:46:40 -06:00
CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK ) ;
if ( IS_ERR ( fallback_tfm ) ) {
pr_warn ( " could not load fallback driver %s \n " ,
2014-05-14 11:40:58 +02:00
crypto_tfm_alg_name ( tfm ) ) ;
2013-11-12 11:46:40 -06:00
return PTR_ERR ( fallback_tfm ) ;
}
ctx - > u . aes . tfm_ablkcipher = fallback_tfm ;
tfm - > crt_ablkcipher . reqsize = sizeof ( struct ccp_aes_req_ctx ) +
fallback_tfm - > base . crt_ablkcipher . reqsize ;
return 0 ;
}
static void ccp_aes_xts_cra_exit ( struct crypto_tfm * tfm )
{
struct ccp_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
if ( ctx - > u . aes . tfm_ablkcipher )
crypto_free_ablkcipher ( ctx - > u . aes . tfm_ablkcipher ) ;
ctx - > u . aes . tfm_ablkcipher = NULL ;
}
static int ccp_register_aes_xts_alg ( struct list_head * head ,
const struct ccp_aes_xts_def * def )
{
struct ccp_crypto_ablkcipher_alg * ccp_alg ;
struct crypto_alg * alg ;
int ret ;
ccp_alg = kzalloc ( sizeof ( * ccp_alg ) , GFP_KERNEL ) ;
if ( ! ccp_alg )
return - ENOMEM ;
INIT_LIST_HEAD ( & ccp_alg - > entry ) ;
alg = & ccp_alg - > alg ;
snprintf ( alg - > cra_name , CRYPTO_MAX_ALG_NAME , " %s " , def - > name ) ;
snprintf ( alg - > cra_driver_name , CRYPTO_MAX_ALG_NAME , " %s " ,
def - > drv_name ) ;
alg - > cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC |
CRYPTO_ALG_KERN_DRIVER_ONLY |
CRYPTO_ALG_NEED_FALLBACK ;
alg - > cra_blocksize = AES_BLOCK_SIZE ;
alg - > cra_ctxsize = sizeof ( struct ccp_ctx ) ;
alg - > cra_priority = CCP_CRA_PRIORITY ;
alg - > cra_type = & crypto_ablkcipher_type ;
alg - > cra_ablkcipher . setkey = ccp_aes_xts_setkey ;
alg - > cra_ablkcipher . encrypt = ccp_aes_xts_encrypt ;
alg - > cra_ablkcipher . decrypt = ccp_aes_xts_decrypt ;
alg - > cra_ablkcipher . min_keysize = AES_MIN_KEY_SIZE * 2 ;
alg - > cra_ablkcipher . max_keysize = AES_MAX_KEY_SIZE * 2 ;
alg - > cra_ablkcipher . ivsize = AES_BLOCK_SIZE ;
alg - > cra_init = ccp_aes_xts_cra_init ;
alg - > cra_exit = ccp_aes_xts_cra_exit ;
alg - > cra_module = THIS_MODULE ;
ret = crypto_register_alg ( alg ) ;
if ( ret ) {
pr_err ( " %s ablkcipher algorithm registration error (%d) \n " ,
2015-02-03 13:07:05 -06:00
alg - > cra_name , ret ) ;
2013-11-12 11:46:40 -06:00
kfree ( ccp_alg ) ;
return ret ;
}
list_add ( & ccp_alg - > entry , head ) ;
return 0 ;
}
int ccp_register_aes_xts_algs ( struct list_head * head )
{
int i , ret ;
for ( i = 0 ; i < ARRAY_SIZE ( aes_xts_algs ) ; i + + ) {
ret = ccp_register_aes_xts_alg ( head , & aes_xts_algs [ i ] ) ;
if ( ret )
return ret ;
}
return 0 ;
}