2013-11-12 11:46:45 -06:00
/*
* AMD Cryptographic Coprocessor ( CCP ) SHA crypto API support
*
2016-03-01 13:49:15 -06:00
* Copyright ( C ) 2013 , 2016 Advanced Micro Devices , Inc .
2013-11-12 11:46:45 -06:00
*
* 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/hash.h>
# include <crypto/internal/hash.h>
# include <crypto/sha.h>
# include <crypto/scatterwalk.h>
# include "ccp-crypto.h"
static int ccp_sha_complete ( struct crypto_async_request * async_req , int ret )
{
struct ahash_request * req = ahash_request_cast ( async_req ) ;
struct crypto_ahash * tfm = crypto_ahash_reqtfm ( req ) ;
struct ccp_sha_req_ctx * rctx = ahash_request_ctx ( req ) ;
unsigned int digest_size = crypto_ahash_digestsize ( tfm ) ;
if ( ret )
goto e_free ;
if ( rctx - > hash_rem ) {
/* Save remaining data to buffer */
2014-01-06 13:34:17 -06:00
unsigned int offset = rctx - > nbytes - rctx - > hash_rem ;
2015-02-03 13:07:05 -06:00
2014-01-06 13:34:17 -06:00
scatterwalk_map_and_copy ( rctx - > buf , rctx - > src ,
offset , rctx - > hash_rem , 0 ) ;
2013-11-12 11:46:45 -06:00
rctx - > buf_count = rctx - > hash_rem ;
2015-02-03 13:07:05 -06:00
} else {
2013-11-12 11:46:45 -06:00
rctx - > buf_count = 0 ;
2015-02-03 13:07:05 -06:00
}
2013-11-12 11:46:45 -06:00
2014-01-06 13:34:11 -06:00
/* Update result area if supplied */
if ( req - > result )
memcpy ( req - > result , rctx - > ctx , digest_size ) ;
2013-11-12 11:46:45 -06:00
e_free :
sg_free_table ( & rctx - > data_sg ) ;
return ret ;
}
static int ccp_do_sha_update ( struct ahash_request * req , unsigned int nbytes ,
unsigned int final )
{
struct crypto_ahash * tfm = crypto_ahash_reqtfm ( req ) ;
2014-01-24 16:18:02 -06:00
struct ccp_ctx * ctx = crypto_ahash_ctx ( tfm ) ;
2013-11-12 11:46:45 -06:00
struct ccp_sha_req_ctx * rctx = ahash_request_ctx ( req ) ;
struct scatterlist * sg ;
unsigned int block_size =
crypto_tfm_alg_blocksize ( crypto_ahash_tfm ( tfm ) ) ;
2014-01-06 13:34:17 -06:00
unsigned int sg_count ;
2014-01-06 13:33:59 -06:00
gfp_t gfp ;
2014-01-06 13:34:17 -06:00
u64 len ;
2013-11-12 11:46:45 -06:00
int ret ;
2014-01-06 13:34:17 -06:00
len = ( u64 ) rctx - > buf_count + ( u64 ) nbytes ;
if ( ! final & & ( len < = block_size ) ) {
2013-11-12 11:46:45 -06:00
scatterwalk_map_and_copy ( rctx - > buf + rctx - > buf_count , req - > src ,
0 , nbytes , 0 ) ;
rctx - > buf_count + = nbytes ;
return 0 ;
}
2014-01-06 13:34:17 -06:00
rctx - > src = req - > src ;
rctx - > nbytes = nbytes ;
2013-11-12 11:46:45 -06:00
rctx - > final = final ;
2014-01-06 13:34:17 -06:00
rctx - > hash_rem = final ? 0 : len & ( block_size - 1 ) ;
rctx - > hash_cnt = len - rctx - > hash_rem ;
if ( ! final & & ! rctx - > hash_rem ) {
2013-11-12 11:46:45 -06:00
/* CCP can't do zero length final, so keep some data around */
rctx - > hash_cnt - = block_size ;
rctx - > hash_rem = block_size ;
}
/* Initialize the context scatterlist */
sg_init_one ( & rctx - > ctx_sg , rctx - > ctx , sizeof ( rctx - > ctx ) ) ;
sg = NULL ;
2014-01-06 13:34:05 -06:00
if ( rctx - > buf_count & & nbytes ) {
/* Build the data scatterlist table - allocate enough entries
* for both data pieces ( buffer and input data )
*/
gfp = req - > base . flags & CRYPTO_TFM_REQ_MAY_SLEEP ?
GFP_KERNEL : GFP_ATOMIC ;
sg_count = sg_nents ( req - > src ) + 1 ;
ret = sg_alloc_table ( & rctx - > data_sg , sg_count , gfp ) ;
if ( ret )
return ret ;
2013-11-12 11:46:45 -06:00
sg_init_one ( & rctx - > buf_sg , rctx - > buf , rctx - > buf_count ) ;
sg = ccp_crypto_sg_table_add ( & rctx - > data_sg , & rctx - > buf_sg ) ;
2015-10-01 16:32:31 -05:00
if ( ! sg ) {
ret = - EINVAL ;
goto e_free ;
}
2013-11-12 11:46:45 -06:00
sg = ccp_crypto_sg_table_add ( & rctx - > data_sg , req - > src ) ;
2015-10-01 16:32:31 -05:00
if ( ! sg ) {
ret = - EINVAL ;
goto e_free ;
}
2013-11-12 11:46:45 -06:00
sg_mark_end ( sg ) ;
2014-01-06 13:34:05 -06:00
sg = rctx - > data_sg . sgl ;
} else if ( rctx - > buf_count ) {
sg_init_one ( & rctx - > buf_sg , rctx - > buf , rctx - > buf_count ) ;
sg = & rctx - > buf_sg ;
} else if ( nbytes ) {
sg = req - > src ;
}
2013-11-12 11:46:45 -06:00
rctx - > msg_bits + = ( rctx - > hash_cnt < < 3 ) ; /* Total in bits */
memset ( & rctx - > cmd , 0 , sizeof ( rctx - > cmd ) ) ;
INIT_LIST_HEAD ( & rctx - > cmd . entry ) ;
rctx - > cmd . engine = CCP_ENGINE_SHA ;
rctx - > cmd . u . sha . type = rctx - > type ;
rctx - > cmd . u . sha . ctx = & rctx - > ctx_sg ;
rctx - > cmd . u . sha . ctx_len = sizeof ( rctx - > ctx ) ;
2014-01-06 13:34:05 -06:00
rctx - > cmd . u . sha . src = sg ;
2013-11-12 11:46:45 -06:00
rctx - > cmd . u . sha . src_len = rctx - > hash_cnt ;
2014-01-24 16:18:02 -06:00
rctx - > cmd . u . sha . opad = ctx - > u . sha . key_len ?
& ctx - > u . sha . opad_sg : NULL ;
rctx - > cmd . u . sha . opad_len = ctx - > u . sha . key_len ?
ctx - > u . sha . opad_count : 0 ;
rctx - > cmd . u . sha . first = rctx - > first ;
2013-11-12 11:46:45 -06:00
rctx - > cmd . u . sha . final = rctx - > final ;
rctx - > cmd . u . sha . msg_bits = rctx - > msg_bits ;
rctx - > first = 0 ;
ret = ccp_crypto_enqueue_request ( & req - > base , & rctx - > cmd ) ;
2015-10-01 16:32:31 -05:00
return ret ;
e_free :
sg_free_table ( & rctx - > data_sg ) ;
2013-11-12 11:46:45 -06:00
return ret ;
}
static int ccp_sha_init ( struct ahash_request * req )
{
struct crypto_ahash * tfm = crypto_ahash_reqtfm ( req ) ;
2014-01-06 13:34:05 -06:00
struct ccp_ctx * ctx = crypto_ahash_ctx ( tfm ) ;
2013-11-12 11:46:45 -06:00
struct ccp_sha_req_ctx * rctx = ahash_request_ctx ( req ) ;
struct ccp_crypto_ahash_alg * alg =
ccp_crypto_ahash_alg ( crypto_ahash_tfm ( tfm ) ) ;
2014-01-06 13:34:05 -06:00
unsigned int block_size =
crypto_tfm_alg_blocksize ( crypto_ahash_tfm ( tfm ) ) ;
2013-11-12 11:46:45 -06:00
memset ( rctx , 0 , sizeof ( * rctx ) ) ;
rctx - > type = alg - > type ;
rctx - > first = 1 ;
2014-01-06 13:34:05 -06:00
if ( ctx - > u . sha . key_len ) {
/* Buffer the HMAC key for first update */
memcpy ( rctx - > buf , ctx - > u . sha . ipad , block_size ) ;
rctx - > buf_count = block_size ;
}
2013-11-12 11:46:45 -06:00
return 0 ;
}
static int ccp_sha_update ( struct ahash_request * req )
{
return ccp_do_sha_update ( req , req - > nbytes , 0 ) ;
}
static int ccp_sha_final ( struct ahash_request * req )
{
return ccp_do_sha_update ( req , 0 , 1 ) ;
}
static int ccp_sha_finup ( struct ahash_request * req )
{
return ccp_do_sha_update ( req , req - > nbytes , 1 ) ;
}
static int ccp_sha_digest ( struct ahash_request * req )
{
2014-01-06 13:34:23 -06:00
int ret ;
2013-11-12 11:46:45 -06:00
2014-01-06 13:34:23 -06:00
ret = ccp_sha_init ( req ) ;
if ( ret )
return ret ;
return ccp_sha_finup ( req ) ;
2013-11-12 11:46:45 -06:00
}
2016-01-12 11:17:38 -06:00
static int ccp_sha_export ( struct ahash_request * req , void * out )
{
struct ccp_sha_req_ctx * rctx = ahash_request_ctx ( req ) ;
2016-02-02 11:38:21 -06:00
struct ccp_sha_exp_ctx state ;
2016-01-12 11:17:38 -06:00
2016-04-13 10:52:25 -05:00
/* Don't let anything leak to 'out' */
memset ( & state , 0 , sizeof ( state ) ) ;
2016-02-02 11:38:21 -06:00
state . type = rctx - > type ;
state . msg_bits = rctx - > msg_bits ;
state . first = rctx - > first ;
memcpy ( state . ctx , rctx - > ctx , sizeof ( state . ctx ) ) ;
state . buf_count = rctx - > buf_count ;
memcpy ( state . buf , rctx - > buf , sizeof ( state . buf ) ) ;
/* 'out' may not be aligned so memcpy from local variable */
memcpy ( out , & state , sizeof ( state ) ) ;
2016-01-12 11:17:38 -06:00
return 0 ;
}
static int ccp_sha_import ( struct ahash_request * req , const void * in )
{
struct ccp_sha_req_ctx * rctx = ahash_request_ctx ( req ) ;
2016-02-02 11:38:21 -06:00
struct ccp_sha_exp_ctx state ;
/* 'in' may not be aligned so memcpy to local variable */
memcpy ( & state , in , sizeof ( state ) ) ;
2016-02-25 16:48:13 -06:00
memset ( rctx , 0 , sizeof ( * rctx ) ) ;
2016-02-02 11:38:21 -06:00
rctx - > type = state . type ;
rctx - > msg_bits = state . msg_bits ;
rctx - > first = state . first ;
memcpy ( rctx - > ctx , state . ctx , sizeof ( rctx - > ctx ) ) ;
rctx - > buf_count = state . buf_count ;
memcpy ( rctx - > buf , state . buf , sizeof ( rctx - > buf ) ) ;
2016-01-12 11:17:38 -06:00
return 0 ;
}
2013-11-12 11:46:45 -06:00
static int ccp_sha_setkey ( struct crypto_ahash * tfm , const u8 * key ,
unsigned int key_len )
{
struct ccp_ctx * ctx = crypto_tfm_ctx ( crypto_ahash_tfm ( tfm ) ) ;
2014-01-24 16:18:02 -06:00
struct crypto_shash * shash = ctx - > u . sha . hmac_tfm ;
2014-09-04 20:39:24 +02:00
SHASH_DESC_ON_STACK ( sdesc , shash ) ;
2014-01-24 16:18:02 -06:00
unsigned int block_size = crypto_shash_blocksize ( shash ) ;
unsigned int digest_size = crypto_shash_digestsize ( shash ) ;
2013-11-12 11:46:45 -06:00
int i , ret ;
/* Set to zero until complete */
ctx - > u . sha . key_len = 0 ;
/* Clear key area to provide zero padding for keys smaller
* than the block size
*/
memset ( ctx - > u . sha . key , 0 , sizeof ( ctx - > u . sha . key ) ) ;
if ( key_len > block_size ) {
/* Must hash the input key */
2014-09-04 20:39:24 +02:00
sdesc - > tfm = shash ;
sdesc - > flags = crypto_ahash_get_flags ( tfm ) &
2014-01-24 16:18:02 -06:00
CRYPTO_TFM_REQ_MAY_SLEEP ;
2014-09-04 20:39:24 +02:00
ret = crypto_shash_digest ( sdesc , key , key_len ,
2014-01-24 16:18:02 -06:00
ctx - > u . sha . key ) ;
2013-11-12 11:46:45 -06:00
if ( ret ) {
crypto_ahash_set_flags ( tfm , CRYPTO_TFM_RES_BAD_KEY_LEN ) ;
return - EINVAL ;
}
key_len = digest_size ;
2015-02-03 13:07:05 -06:00
} else {
2013-11-12 11:46:45 -06:00
memcpy ( ctx - > u . sha . key , key , key_len ) ;
2015-02-03 13:07:05 -06:00
}
2013-11-12 11:46:45 -06:00
for ( i = 0 ; i < block_size ; i + + ) {
ctx - > u . sha . ipad [ i ] = ctx - > u . sha . key [ i ] ^ 0x36 ;
ctx - > u . sha . opad [ i ] = ctx - > u . sha . key [ i ] ^ 0x5c ;
}
2014-01-24 16:18:02 -06:00
sg_init_one ( & ctx - > u . sha . opad_sg , ctx - > u . sha . opad , block_size ) ;
ctx - > u . sha . opad_count = block_size ;
2013-11-12 11:46:45 -06:00
ctx - > u . sha . key_len = key_len ;
return 0 ;
}
static int ccp_sha_cra_init ( struct crypto_tfm * tfm )
{
struct ccp_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
struct crypto_ahash * ahash = __crypto_ahash_cast ( tfm ) ;
ctx - > complete = ccp_sha_complete ;
ctx - > u . sha . key_len = 0 ;
crypto_ahash_set_reqsize ( ahash , sizeof ( struct ccp_sha_req_ctx ) ) ;
return 0 ;
}
static void ccp_sha_cra_exit ( struct crypto_tfm * tfm )
{
}
static int ccp_hmac_sha_cra_init ( struct crypto_tfm * tfm )
{
struct ccp_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
struct ccp_crypto_ahash_alg * alg = ccp_crypto_ahash_alg ( tfm ) ;
2014-01-24 16:18:02 -06:00
struct crypto_shash * hmac_tfm ;
2013-11-12 11:46:45 -06:00
2014-01-24 16:18:02 -06:00
hmac_tfm = crypto_alloc_shash ( alg - > child_alg , 0 , 0 ) ;
2013-11-12 11:46:45 -06:00
if ( IS_ERR ( hmac_tfm ) ) {
pr_warn ( " could not load driver %s need for HMAC support \n " ,
alg - > child_alg ) ;
return PTR_ERR ( hmac_tfm ) ;
}
ctx - > u . sha . hmac_tfm = hmac_tfm ;
return ccp_sha_cra_init ( tfm ) ;
}
static void ccp_hmac_sha_cra_exit ( struct crypto_tfm * tfm )
{
struct ccp_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
if ( ctx - > u . sha . hmac_tfm )
2014-01-24 16:18:02 -06:00
crypto_free_shash ( ctx - > u . sha . hmac_tfm ) ;
2013-11-12 11:46:45 -06:00
ccp_sha_cra_exit ( tfm ) ;
}
struct ccp_sha_def {
2016-03-01 13:49:15 -06:00
unsigned int version ;
2013-11-12 11:46:45 -06:00
const char * name ;
const char * drv_name ;
enum ccp_sha_type type ;
u32 digest_size ;
u32 block_size ;
} ;
static struct ccp_sha_def sha_algs [ ] = {
{
2016-03-01 13:49:15 -06:00
. version = CCP_VERSION ( 3 , 0 ) ,
2013-11-12 11:46:45 -06:00
. name = " sha1 " ,
. drv_name = " sha1-ccp " ,
. type = CCP_SHA_TYPE_1 ,
. digest_size = SHA1_DIGEST_SIZE ,
. block_size = SHA1_BLOCK_SIZE ,
} ,
{
2016-03-01 13:49:15 -06:00
. version = CCP_VERSION ( 3 , 0 ) ,
2013-11-12 11:46:45 -06:00
. name = " sha224 " ,
. drv_name = " sha224-ccp " ,
. type = CCP_SHA_TYPE_224 ,
. digest_size = SHA224_DIGEST_SIZE ,
. block_size = SHA224_BLOCK_SIZE ,
} ,
{
2016-03-01 13:49:15 -06:00
. version = CCP_VERSION ( 3 , 0 ) ,
2013-11-12 11:46:45 -06:00
. name = " sha256 " ,
. drv_name = " sha256-ccp " ,
. type = CCP_SHA_TYPE_256 ,
. digest_size = SHA256_DIGEST_SIZE ,
. block_size = SHA256_BLOCK_SIZE ,
} ,
} ;
static int ccp_register_hmac_alg ( struct list_head * head ,
const struct ccp_sha_def * def ,
const struct ccp_crypto_ahash_alg * base_alg )
{
struct ccp_crypto_ahash_alg * ccp_alg ;
struct ahash_alg * alg ;
struct hash_alg_common * halg ;
struct crypto_alg * base ;
int ret ;
ccp_alg = kzalloc ( sizeof ( * ccp_alg ) , GFP_KERNEL ) ;
if ( ! ccp_alg )
return - ENOMEM ;
/* Copy the base algorithm and only change what's necessary */
2013-12-09 20:08:19 +08:00
* ccp_alg = * base_alg ;
2013-11-12 11:46:45 -06:00
INIT_LIST_HEAD ( & ccp_alg - > entry ) ;
strncpy ( ccp_alg - > child_alg , def - > name , CRYPTO_MAX_ALG_NAME ) ;
alg = & ccp_alg - > alg ;
alg - > setkey = ccp_sha_setkey ;
halg = & alg - > halg ;
base = & halg - > base ;
snprintf ( base - > cra_name , CRYPTO_MAX_ALG_NAME , " hmac(%s) " , def - > name ) ;
snprintf ( base - > cra_driver_name , CRYPTO_MAX_ALG_NAME , " hmac-%s " ,
def - > drv_name ) ;
base - > cra_init = ccp_hmac_sha_cra_init ;
base - > cra_exit = ccp_hmac_sha_cra_exit ;
ret = crypto_register_ahash ( alg ) ;
if ( ret ) {
pr_err ( " %s ahash algorithm registration error (%d) \n " ,
2015-02-03 13:07:05 -06:00
base - > cra_name , ret ) ;
2013-11-12 11:46:45 -06:00
kfree ( ccp_alg ) ;
return ret ;
}
list_add ( & ccp_alg - > entry , head ) ;
return ret ;
}
static int ccp_register_sha_alg ( struct list_head * head ,
const struct ccp_sha_def * def )
{
struct ccp_crypto_ahash_alg * ccp_alg ;
struct ahash_alg * alg ;
struct hash_alg_common * halg ;
struct crypto_alg * base ;
int ret ;
ccp_alg = kzalloc ( sizeof ( * ccp_alg ) , GFP_KERNEL ) ;
if ( ! ccp_alg )
return - ENOMEM ;
INIT_LIST_HEAD ( & ccp_alg - > entry ) ;
ccp_alg - > type = def - > type ;
alg = & ccp_alg - > alg ;
alg - > init = ccp_sha_init ;
alg - > update = ccp_sha_update ;
alg - > final = ccp_sha_final ;
alg - > finup = ccp_sha_finup ;
alg - > digest = ccp_sha_digest ;
2016-01-12 11:17:38 -06:00
alg - > export = ccp_sha_export ;
alg - > import = ccp_sha_import ;
2013-11-12 11:46:45 -06:00
halg = & alg - > halg ;
halg - > digestsize = def - > digest_size ;
2016-01-29 12:45:14 -06:00
halg - > statesize = sizeof ( struct ccp_sha_exp_ctx ) ;
2013-11-12 11:46:45 -06:00
base = & halg - > base ;
snprintf ( base - > cra_name , CRYPTO_MAX_ALG_NAME , " %s " , def - > name ) ;
snprintf ( base - > cra_driver_name , CRYPTO_MAX_ALG_NAME , " %s " ,
def - > drv_name ) ;
base - > cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC |
CRYPTO_ALG_KERN_DRIVER_ONLY |
CRYPTO_ALG_NEED_FALLBACK ;
base - > cra_blocksize = def - > block_size ;
base - > cra_ctxsize = sizeof ( struct ccp_ctx ) ;
base - > cra_priority = CCP_CRA_PRIORITY ;
base - > cra_type = & crypto_ahash_type ;
base - > cra_init = ccp_sha_cra_init ;
base - > cra_exit = ccp_sha_cra_exit ;
base - > cra_module = THIS_MODULE ;
ret = crypto_register_ahash ( alg ) ;
if ( ret ) {
pr_err ( " %s ahash algorithm registration error (%d) \n " ,
2015-02-03 13:07:05 -06:00
base - > cra_name , ret ) ;
2013-11-12 11:46:45 -06:00
kfree ( ccp_alg ) ;
return ret ;
}
list_add ( & ccp_alg - > entry , head ) ;
ret = ccp_register_hmac_alg ( head , def , ccp_alg ) ;
return ret ;
}
int ccp_register_sha_algs ( struct list_head * head )
{
int i , ret ;
2016-03-01 13:49:15 -06:00
unsigned int ccpversion = ccp_version ( ) ;
2013-11-12 11:46:45 -06:00
for ( i = 0 ; i < ARRAY_SIZE ( sha_algs ) ; i + + ) {
2016-03-01 13:49:15 -06:00
if ( sha_algs [ i ] . version > ccpversion )
continue ;
2013-11-12 11:46:45 -06:00
ret = ccp_register_sha_alg ( head , & sha_algs [ i ] ) ;
if ( ret )
return ret ;
}
return 0 ;
}