2005-04-17 02:20:36 +04:00
/*
* Cryptographic API .
*
* Support for VIA PadLock hardware crypto engine .
*
* Copyright ( c ) 2004 Michal Ludvig < michal @ logix . cz >
*
*/
2006-08-21 15:38:42 +04:00
# include <crypto/algapi.h>
2007-10-17 19:18:57 +04:00
# include <crypto/aes.h>
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/interrupt.h>
2005-07-07 00:52:27 +04:00
# include <linux/kernel.h>
2005-04-17 02:20:36 +04:00
# include <asm/byteorder.h>
# include "padlock.h"
2006-07-15 04:23:49 +04:00
/* Control word. */
struct cword {
unsigned int __attribute__ ( ( __packed__ ) )
rounds : 4 ,
algo : 3 ,
keygen : 1 ,
interm : 1 ,
encdec : 1 ,
ksize : 2 ;
} __attribute__ ( ( __aligned__ ( PADLOCK_ALIGNMENT ) ) ) ;
2006-07-15 05:08:50 +04:00
/* Whenever making any changes to the following
* structure * make sure * you keep E , d_data
2008-04-01 17:24:50 +04:00
* and cword aligned on 16 Bytes boundaries and
* the Hardware can access 16 * 16 bytes of E and d_data
* ( only the first 15 * 16 bytes matter but the HW reads
* more ) .
*/
2005-04-17 02:20:36 +04:00
struct aes_ctx {
2008-04-01 17:24:50 +04:00
u32 E [ AES_MAX_KEYLENGTH_U32 ]
__attribute__ ( ( __aligned__ ( PADLOCK_ALIGNMENT ) ) ) ;
u32 d_data [ AES_MAX_KEYLENGTH_U32 ]
__attribute__ ( ( __aligned__ ( PADLOCK_ALIGNMENT ) ) ) ;
2005-07-07 00:52:27 +04:00
struct {
struct cword encrypt ;
struct cword decrypt ;
} cword ;
2006-05-16 16:20:34 +04:00
u32 * D ;
2005-04-17 02:20:36 +04:00
} ;
/* Tells whether the ACE is capable to generate
the extended key for a given key_len . */
static inline int
aes_hw_extkey_available ( uint8_t key_len )
{
/* TODO: We should check the actual CPU model/stepping
as it ' s possible that the capability will be
added in the next CPU revisions . */
if ( key_len = = 16 )
return 1 ;
return 0 ;
}
2006-08-21 15:38:42 +04:00
static inline struct aes_ctx * aes_ctx_common ( void * ctx )
2005-07-07 00:52:27 +04:00
{
2006-08-21 15:38:42 +04:00
unsigned long addr = ( unsigned long ) ctx ;
2006-01-25 14:34:01 +03:00
unsigned long align = PADLOCK_ALIGNMENT ;
if ( align < = crypto_tfm_ctx_alignment ( ) )
align = 1 ;
2006-05-16 16:09:29 +04:00
return ( struct aes_ctx * ) ALIGN ( addr , align ) ;
2005-07-07 00:52:27 +04:00
}
2006-08-21 15:38:42 +04:00
static inline struct aes_ctx * aes_ctx ( struct crypto_tfm * tfm )
{
return aes_ctx_common ( crypto_tfm_ctx ( tfm ) ) ;
}
static inline struct aes_ctx * blk_aes_ctx ( struct crypto_blkcipher * tfm )
{
return aes_ctx_common ( crypto_blkcipher_ctx ( tfm ) ) ;
}
2006-05-16 16:09:29 +04:00
static int aes_set_key ( struct crypto_tfm * tfm , const u8 * in_key ,
2006-08-13 08:16:39 +04:00
unsigned int key_len )
2005-04-17 02:20:36 +04:00
{
2006-05-16 16:09:29 +04:00
struct aes_ctx * ctx = aes_ctx ( tfm ) ;
2005-10-30 13:25:15 +03:00
const __le32 * key = ( const __le32 * ) in_key ;
2006-08-13 08:16:39 +04:00
u32 * flags = & tfm - > crt_flags ;
2008-04-01 17:24:50 +04:00
struct crypto_aes_ctx gen_aes ;
2005-04-17 02:20:36 +04:00
2006-08-13 08:16:39 +04:00
if ( key_len % 8 ) {
2005-04-17 02:20:36 +04:00
* flags | = CRYPTO_TFM_RES_BAD_KEY_LEN ;
return - EINVAL ;
}
2005-07-07 00:52:27 +04:00
/*
* If the hardware is capable of generating the extended key
* itself we must supply the plain key for both encryption
* and decryption .
*/
2006-05-16 16:20:34 +04:00
ctx - > D = ctx - > E ;
2005-04-17 02:20:36 +04:00
2008-04-01 17:24:50 +04:00
ctx - > E [ 0 ] = le32_to_cpu ( key [ 0 ] ) ;
ctx - > E [ 1 ] = le32_to_cpu ( key [ 1 ] ) ;
ctx - > E [ 2 ] = le32_to_cpu ( key [ 2 ] ) ;
ctx - > E [ 3 ] = le32_to_cpu ( key [ 3 ] ) ;
2005-04-17 02:20:36 +04:00
2005-07-07 00:52:27 +04:00
/* Prepare control words. */
memset ( & ctx - > cword , 0 , sizeof ( ctx - > cword ) ) ;
ctx - > cword . decrypt . encdec = 1 ;
ctx - > cword . encrypt . rounds = 10 + ( key_len - 16 ) / 4 ;
ctx - > cword . decrypt . rounds = ctx - > cword . encrypt . rounds ;
ctx - > cword . encrypt . ksize = ( key_len - 16 ) / 8 ;
ctx - > cword . decrypt . ksize = ctx - > cword . encrypt . ksize ;
2005-04-17 02:20:36 +04:00
/* Don't generate extended keys if the hardware can do it. */
if ( aes_hw_extkey_available ( key_len ) )
return 0 ;
2005-07-07 00:52:27 +04:00
ctx - > D = ctx - > d_data ;
ctx - > cword . encrypt . keygen = 1 ;
ctx - > cword . decrypt . keygen = 1 ;
2008-04-01 17:24:50 +04:00
if ( crypto_aes_expand_key ( & gen_aes , in_key , key_len ) ) {
* flags | = CRYPTO_TFM_RES_BAD_KEY_LEN ;
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2008-04-01 17:24:50 +04:00
memcpy ( ctx - > E , gen_aes . key_enc , AES_MAX_KEYLENGTH ) ;
memcpy ( ctx - > D , gen_aes . key_dec , AES_MAX_KEYLENGTH ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* ====== Encryption/decryption routines ====== */
2005-07-07 00:52:43 +04:00
/* These are the real call to PadLock. */
2007-12-26 16:04:44 +03:00
static inline void padlock_reset_key ( void )
{
asm volatile ( " pushfl; popfl " ) ;
}
2007-12-28 03:05:46 +03:00
static inline void padlock_xcrypt ( const u8 * input , u8 * output , void * key ,
void * control_word )
{
asm volatile ( " .byte 0xf3,0x0f,0xa7,0xc8 " /* rep xcryptecb */
: " +S " ( input ) , " +D " ( output )
: " d " ( control_word ) , " b " ( key ) , " c " ( 1 ) ) ;
}
static void aes_crypt_copy ( const u8 * in , u8 * out , u32 * key , struct cword * cword )
{
2008-01-11 00:09:35 +03:00
u8 buf [ AES_BLOCK_SIZE * 2 + PADLOCK_ALIGNMENT - 1 ] ;
u8 * tmp = PTR_ALIGN ( & buf [ 0 ] , PADLOCK_ALIGNMENT ) ;
2007-12-28 03:05:46 +03:00
memcpy ( tmp , in , AES_BLOCK_SIZE ) ;
padlock_xcrypt ( tmp , out , key , cword ) ;
}
static inline void aes_crypt ( const u8 * in , u8 * out , u32 * key ,
struct cword * cword )
{
/* padlock_xcrypt requires at least two blocks of data. */
if ( unlikely ( ! ( ( ( unsigned long ) in ^ ( PAGE_SIZE - AES_BLOCK_SIZE ) ) &
( PAGE_SIZE - 1 ) ) ) ) {
aes_crypt_copy ( in , out , key , cword ) ;
return ;
}
padlock_xcrypt ( in , out , key , cword ) ;
}
2005-07-07 00:52:27 +04:00
static inline void padlock_xcrypt_ecb ( const u8 * input , u8 * output , void * key ,
void * control_word , u32 count )
2005-04-17 02:20:36 +04:00
{
2007-12-28 03:05:46 +03:00
if ( count = = 1 ) {
aes_crypt ( input , output , key , control_word ) ;
return ;
}
asm volatile ( " test $1, %%cl; "
" je 1f; "
" lea -1(%%ecx), %%eax; "
" mov $1, %%ecx; "
" .byte 0xf3,0x0f,0xa7,0xc8; " /* rep xcryptecb */
" mov %%eax, %%ecx; "
" 1: "
" .byte 0xf3,0x0f,0xa7,0xc8 " /* rep xcryptecb */
2005-04-17 02:20:36 +04:00
: " +S " ( input ) , " +D " ( output )
2007-12-28 03:05:46 +03:00
: " d " ( control_word ) , " b " ( key ) , " c " ( count )
: " ax " ) ;
2005-04-17 02:20:36 +04:00
}
2005-07-07 00:54:09 +04:00
static inline u8 * padlock_xcrypt_cbc ( const u8 * input , u8 * output , void * key ,
u8 * iv , void * control_word , u32 count )
2005-07-07 00:52:43 +04:00
{
/* rep xcryptcbc */
asm volatile ( " .byte 0xf3,0x0f,0xa7,0xd0 "
: " +S " ( input ) , " +D " ( output ) , " +a " ( iv )
: " d " ( control_word ) , " b " ( key ) , " c " ( count ) ) ;
2005-07-07 00:54:09 +04:00
return iv ;
2005-07-07 00:52:43 +04:00
}
2006-05-16 16:09:29 +04:00
static void aes_encrypt ( struct crypto_tfm * tfm , u8 * out , const u8 * in )
2005-04-17 02:20:36 +04:00
{
2006-05-16 16:09:29 +04:00
struct aes_ctx * ctx = aes_ctx ( tfm ) ;
2007-12-26 16:04:44 +03:00
padlock_reset_key ( ) ;
2007-12-28 03:05:46 +03:00
aes_crypt ( in , out , ctx - > E , & ctx - > cword . encrypt ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-16 16:09:29 +04:00
static void aes_decrypt ( struct crypto_tfm * tfm , u8 * out , const u8 * in )
2005-04-17 02:20:36 +04:00
{
2006-05-16 16:09:29 +04:00
struct aes_ctx * ctx = aes_ctx ( tfm ) ;
2007-12-26 16:04:44 +03:00
padlock_reset_key ( ) ;
2007-12-28 03:05:46 +03:00
aes_crypt ( in , out , ctx - > D , & ctx - > cword . decrypt ) ;
2005-04-17 02:20:36 +04:00
}
static struct crypto_alg aes_alg = {
. cra_name = " aes " ,
2005-11-05 10:06:26 +03:00
. cra_driver_name = " aes-padlock " ,
2006-07-15 04:23:49 +04:00
. cra_priority = PADLOCK_CRA_PRIORITY ,
2005-04-17 02:20:36 +04:00
. cra_flags = CRYPTO_ALG_TYPE_CIPHER ,
. cra_blocksize = AES_BLOCK_SIZE ,
2005-07-07 00:53:29 +04:00
. cra_ctxsize = sizeof ( struct aes_ctx ) ,
2005-07-07 00:52:27 +04:00
. cra_alignmask = PADLOCK_ALIGNMENT - 1 ,
2005-04-17 02:20:36 +04:00
. cra_module = THIS_MODULE ,
. cra_list = LIST_HEAD_INIT ( aes_alg . cra_list ) ,
. cra_u = {
. cipher = {
. cia_min_keysize = AES_MIN_KEY_SIZE ,
. cia_max_keysize = AES_MAX_KEY_SIZE ,
. cia_setkey = aes_set_key ,
. cia_encrypt = aes_encrypt ,
2005-07-07 00:52:43 +04:00
. cia_decrypt = aes_decrypt ,
2005-04-17 02:20:36 +04:00
}
}
} ;
2006-08-21 15:38:42 +04:00
static int ecb_aes_encrypt ( struct blkcipher_desc * desc ,
struct scatterlist * dst , struct scatterlist * src ,
unsigned int nbytes )
{
struct aes_ctx * ctx = blk_aes_ctx ( desc - > tfm ) ;
struct blkcipher_walk walk ;
int err ;
2007-12-26 16:04:44 +03:00
padlock_reset_key ( ) ;
2006-08-21 15:38:42 +04:00
blkcipher_walk_init ( & walk , dst , src , nbytes ) ;
err = blkcipher_walk_virt ( desc , & walk ) ;
while ( ( nbytes = walk . nbytes ) ) {
padlock_xcrypt_ecb ( walk . src . virt . addr , walk . dst . virt . addr ,
ctx - > E , & ctx - > cword . encrypt ,
nbytes / AES_BLOCK_SIZE ) ;
nbytes & = AES_BLOCK_SIZE - 1 ;
err = blkcipher_walk_done ( desc , & walk , nbytes ) ;
}
return err ;
}
static int ecb_aes_decrypt ( struct blkcipher_desc * desc ,
struct scatterlist * dst , struct scatterlist * src ,
unsigned int nbytes )
{
struct aes_ctx * ctx = blk_aes_ctx ( desc - > tfm ) ;
struct blkcipher_walk walk ;
int err ;
2007-12-26 16:04:44 +03:00
padlock_reset_key ( ) ;
2006-08-21 15:38:42 +04:00
blkcipher_walk_init ( & walk , dst , src , nbytes ) ;
err = blkcipher_walk_virt ( desc , & walk ) ;
while ( ( nbytes = walk . nbytes ) ) {
padlock_xcrypt_ecb ( walk . src . virt . addr , walk . dst . virt . addr ,
ctx - > D , & ctx - > cword . decrypt ,
nbytes / AES_BLOCK_SIZE ) ;
nbytes & = AES_BLOCK_SIZE - 1 ;
err = blkcipher_walk_done ( desc , & walk , nbytes ) ;
}
return err ;
}
static struct crypto_alg ecb_aes_alg = {
. cra_name = " ecb(aes) " ,
. cra_driver_name = " ecb-aes-padlock " ,
. cra_priority = PADLOCK_COMPOSITE_PRIORITY ,
. cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER ,
. cra_blocksize = AES_BLOCK_SIZE ,
. cra_ctxsize = sizeof ( struct aes_ctx ) ,
. cra_alignmask = PADLOCK_ALIGNMENT - 1 ,
. cra_type = & crypto_blkcipher_type ,
. cra_module = THIS_MODULE ,
. cra_list = LIST_HEAD_INIT ( ecb_aes_alg . cra_list ) ,
. cra_u = {
. blkcipher = {
. min_keysize = AES_MIN_KEY_SIZE ,
. max_keysize = AES_MAX_KEY_SIZE ,
. setkey = aes_set_key ,
. encrypt = ecb_aes_encrypt ,
. decrypt = ecb_aes_decrypt ,
}
}
} ;
static int cbc_aes_encrypt ( struct blkcipher_desc * desc ,
struct scatterlist * dst , struct scatterlist * src ,
unsigned int nbytes )
{
struct aes_ctx * ctx = blk_aes_ctx ( desc - > tfm ) ;
struct blkcipher_walk walk ;
int err ;
2007-12-26 16:04:44 +03:00
padlock_reset_key ( ) ;
2006-08-21 15:38:42 +04:00
blkcipher_walk_init ( & walk , dst , src , nbytes ) ;
err = blkcipher_walk_virt ( desc , & walk ) ;
while ( ( nbytes = walk . nbytes ) ) {
u8 * iv = padlock_xcrypt_cbc ( walk . src . virt . addr ,
walk . dst . virt . addr , ctx - > E ,
walk . iv , & ctx - > cword . encrypt ,
nbytes / AES_BLOCK_SIZE ) ;
memcpy ( walk . iv , iv , AES_BLOCK_SIZE ) ;
nbytes & = AES_BLOCK_SIZE - 1 ;
err = blkcipher_walk_done ( desc , & walk , nbytes ) ;
}
return err ;
}
static int cbc_aes_decrypt ( struct blkcipher_desc * desc ,
struct scatterlist * dst , struct scatterlist * src ,
unsigned int nbytes )
{
struct aes_ctx * ctx = blk_aes_ctx ( desc - > tfm ) ;
struct blkcipher_walk walk ;
int err ;
2007-12-26 16:04:44 +03:00
padlock_reset_key ( ) ;
2006-08-21 15:38:42 +04:00
blkcipher_walk_init ( & walk , dst , src , nbytes ) ;
err = blkcipher_walk_virt ( desc , & walk ) ;
while ( ( nbytes = walk . nbytes ) ) {
padlock_xcrypt_cbc ( walk . src . virt . addr , walk . dst . virt . addr ,
ctx - > D , walk . iv , & ctx - > cword . decrypt ,
nbytes / AES_BLOCK_SIZE ) ;
nbytes & = AES_BLOCK_SIZE - 1 ;
err = blkcipher_walk_done ( desc , & walk , nbytes ) ;
}
return err ;
}
static struct crypto_alg cbc_aes_alg = {
. cra_name = " cbc(aes) " ,
. cra_driver_name = " cbc-aes-padlock " ,
. cra_priority = PADLOCK_COMPOSITE_PRIORITY ,
. cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER ,
. cra_blocksize = AES_BLOCK_SIZE ,
. cra_ctxsize = sizeof ( struct aes_ctx ) ,
. cra_alignmask = PADLOCK_ALIGNMENT - 1 ,
. cra_type = & crypto_blkcipher_type ,
. cra_module = THIS_MODULE ,
. cra_list = LIST_HEAD_INIT ( cbc_aes_alg . cra_list ) ,
. cra_u = {
. blkcipher = {
. min_keysize = AES_MIN_KEY_SIZE ,
. max_keysize = AES_MAX_KEY_SIZE ,
. ivsize = AES_BLOCK_SIZE ,
. setkey = aes_set_key ,
. encrypt = cbc_aes_encrypt ,
. decrypt = cbc_aes_decrypt ,
}
}
} ;
2006-08-06 16:46:20 +04:00
static int __init padlock_init ( void )
2005-04-17 02:20:36 +04:00
{
2006-08-06 16:46:20 +04:00
int ret ;
if ( ! cpu_has_xcrypt ) {
2008-07-03 15:03:31 +04:00
printk ( KERN_NOTICE PFX " VIA PadLock not detected. \n " ) ;
2006-08-06 16:46:20 +04:00
return - ENODEV ;
}
if ( ! cpu_has_xcrypt_enabled ) {
2008-07-03 15:03:31 +04:00
printk ( KERN_NOTICE PFX " VIA PadLock detected, but not enabled. Hmm, strange... \n " ) ;
2006-08-06 16:46:20 +04:00
return - ENODEV ;
}
2005-04-17 02:20:36 +04:00
2006-08-21 15:38:42 +04:00
if ( ( ret = crypto_register_alg ( & aes_alg ) ) )
goto aes_err ;
if ( ( ret = crypto_register_alg ( & ecb_aes_alg ) ) )
goto ecb_aes_err ;
if ( ( ret = crypto_register_alg ( & cbc_aes_alg ) ) )
goto cbc_aes_err ;
2006-08-06 16:46:20 +04:00
printk ( KERN_NOTICE PFX " Using VIA PadLock ACE for AES algorithm. \n " ) ;
2006-08-21 15:38:42 +04:00
out :
2006-08-06 16:46:20 +04:00
return ret ;
2006-08-21 15:38:42 +04:00
cbc_aes_err :
crypto_unregister_alg ( & ecb_aes_alg ) ;
ecb_aes_err :
crypto_unregister_alg ( & aes_alg ) ;
aes_err :
printk ( KERN_ERR PFX " VIA PadLock AES initialization failed. \n " ) ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2006-08-06 16:46:20 +04:00
static void __exit padlock_fini ( void )
2005-04-17 02:20:36 +04:00
{
2006-08-21 15:38:42 +04:00
crypto_unregister_alg ( & cbc_aes_alg ) ;
crypto_unregister_alg ( & ecb_aes_alg ) ;
2005-04-17 02:20:36 +04:00
crypto_unregister_alg ( & aes_alg ) ;
}
2006-08-06 16:46:20 +04:00
module_init ( padlock_init ) ;
module_exit ( padlock_fini ) ;
MODULE_DESCRIPTION ( " VIA PadLock AES algorithm support " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Michal Ludvig " ) ;
2007-10-05 12:52:01 +04:00
MODULE_ALIAS ( " aes " ) ;