/* * Cryptographic API. * * Support for Infineon DEU hardware crypto engine. * * Copyright (c) 2005 Johannes Doering , INFINEON * * Key expansion routine taken from crypto/aes.c * * 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. * * --------------------------------------------------------------------------- * Copyright (c) 2002, Dr Brian Gladman , Worcester, UK. * All rights reserved. * * LICENSE TERMS * * The free distribution and use of this software in both source and binary * form is allowed (with or without changes) provided that: * * 1. distributions of this source code include the above copyright * notice, this list of conditions and the following disclaimer; * * 2. distributions in binary form include the above copyright * notice, this list of conditions and the following disclaimer * in the documentation and/or other associated materials; * * 3. the copyright holder's name is not used to endorse products * built using this software without specific written permission. * * ALTERNATIVELY, provided that this notice is retained in full, this product * may be distributed under the terms of the GNU General Public License (GPL), * in which case the provisions of the GPL apply INSTEAD OF those given above. * * DISCLAIMER * * This software is provided 'as is' with no explicit or implied warranties * in respect of its properties, including, but not limited to, correctness * and/or fitness for purpose. * --------------------------------------------------------------------------- ** HISTORY ** $Date $Author $Comment * 9th Nov 2007 Teh Kok How Bug fixes * --------------------------------------------------------------------------- */ /** \addtogroup AMAZON_S_DEU \ingroup AMAZON_S_BSP \brief amazon_s deu driver module */ /*! \file amazon_s_deu_aes.c \ingroup AMAZON_S_DEU \brief aes driver file */ /*! \addtogroup AMAZON_S_DEU_FUNCTIONS \ingroup AMAZON_S_DEU \brief amazon_s deu driver functions */ #include #include #include #include #include #include #include #include #include #include #include #if 0 #define CRTCL_SECT_INIT #define CRTCL_SECT_START local_irq_save(flag) #define CRTCL_SECT_END local_irq_restore(flag) #else static spinlock_t lock; #define CRTCL_SECT_INIT spin_lock_init(&lock) #define CRTCL_SECT_START spin_lock_irqsave(&lock, flag) #define CRTCL_SECT_END spin_unlock_irqrestore(&lock, flag) #endif #define AES_MIN_KEY_SIZE 16 #define AES_MAX_KEY_SIZE 32 #define AES_BLOCK_SIZE 16 #define CTR_RFC3686_NONCE_SIZE 4 #define CTR_RFC3686_IV_SIZE 8 #define CTR_RFC3686_MAX_KEY_SIZE (AES_MAX_KEY_SIZE + CTR_RFC3686_NONCE_SIZE) //#define CRYPTO_DEBUG #ifdef CRYPTO_DEBUG extern char debug_level; #define DPRINTF(level, format, args...) if (level < debug_level) printk(KERN_INFO "[%s %s %d]: " format, __FILE__, __func__, __LINE__, ##args); #else #define DPRINTF(level, format, args...) #endif #ifdef CONFIG_CRYPTO_DEV_AMAZON_S_AES #include #include #define AES_START AMAZON_S_AES_CON #endif #ifdef CONFIG_CRYPTO_DEV_AMAZON_S_DMA #include #include #include #endif struct aes_ctx { int key_length; u32 buf[AES_MAX_KEY_SIZE]; u8 nonce[CTR_RFC3686_NONCE_SIZE]; }; extern int disable_multiblock; extern int disable_deudma; extern int deu_dma_release (void); /*! \fn static u32 endian_swap(u32 input) \ingroup AMAZON_S_DEU_FUNCTIONS \brief perform dword level endian swap \param input value of dword that requires to be swapped */ static u32 endian_swap(u32 input) { #ifdef CONFIG_CRYPTO_DEV_AMAZON_S_DMA u8 *ptr = (u8 *)&input; return ((ptr[3] << 24) | (ptr[2] << 16) | (ptr[1] << 8) | ptr[0]); #else return input; #endif } /*! \fn static int aes_set_key (struct crypto_tfm *tfm, const uint8_t *in_key, unsigned int key_len) \ingroup AMAZON_S_DEU_FUNCTIONS \brief sets AES key \param tfm linux crypto algo transform \param in_key input key \param key_len key lengths of 16, 24 and 32 bytes supported */ static int aes_set_key (struct crypto_tfm *tfm, const uint8_t *in_key, unsigned int key_len) { struct aes_ctx *ctx = crypto_tfm_ctx(tfm); u32 *flags = &tfm->crt_flags; printk("set_key in %s\n", __FILE__); if (key_len != 16 && key_len != 24 && key_len != 32) { *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; return -EINVAL; } ctx->key_length = key_len; DPRINTF(0, "ctx @%p, key_len %d, ctx->key_length %d\n", ctx, key_len, ctx->key_length); memcpy ((u8 *) (ctx->buf), in_key, key_len); return 0; } /*! \fn static int ctr_rfc3686_aes_set_key (struct crypto_tfm *tfm, const uint8_t *in_key, unsigned int key_len) \ingroup AMAZON_S_DEU_FUNCTIONS \brief sets RFC3686 key \param tfm linux crypto algo transform \param in_key input key \param key_len key lengths of 20, 28 and 36 bytes supported; last 4 bytes is nonce */ static int ctr_rfc3686_aes_set_key (struct crypto_tfm *tfm, const uint8_t *in_key, unsigned int key_len) { struct aes_ctx *ctx = crypto_tfm_ctx(tfm); u32 *flags = &tfm->crt_flags; printk("ctr_rfc3686_aes_set_key in %s\n", __FILE__); memcpy(ctx->nonce, in_key + (key_len - CTR_RFC3686_NONCE_SIZE), CTR_RFC3686_NONCE_SIZE); key_len -= CTR_RFC3686_NONCE_SIZE; // remove 4 bytes of nonce if (key_len != 16 && key_len != 24 && key_len != 32) { *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; return -EINVAL; } ctx->key_length = key_len; memcpy ((u8 *) (ctx->buf), in_key, key_len); return 0; } /*! \fn static void amazon_s_deu_aes (void *ctx_arg, uint8_t *out_arg, const uint8_t *in_arg, uint8_t *iv_arg, size_t nbytes, int encdec, int mode) \ingroup AMAZON_S_DEU_FUNCTIONS \brief main interface to AES hardware \param ctx_arg crypto algo context \param out_arg output bytestream \param in_arg input bytestream \param iv_arg initialization vector \param nbytes length of bytestream \param encdec 1 for encrypt; 0 for decrypt \param mode operation mode such as ebc, cbc, ctr */ #ifndef CONFIG_CRYPTO_DEV_AMAZON_S_DMA static void amazon_s_deu_aes (void *ctx_arg, uint8_t *out_arg, const uint8_t *in_arg, uint8_t *iv_arg, size_t nbytes, int encdec, int mode) #else static void amazon_s_deu_aes_core (void *ctx_arg, uint8_t *out_arg, const uint8_t *in_arg, uint8_t *iv_arg, size_t nbytes, int encdec, int mode) #endif { /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ volatile struct aes_t *aes = (volatile struct aes_t *) AES_START; struct aes_ctx *ctx = (struct aes_ctx *)ctx_arg; u32 *in_key = ctx->buf; unsigned long flag; /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int key_len = ctx->key_length; CRTCL_SECT_START; /* 128, 192 or 256 bit key length */ aes->controlr.K = key_len / 8 - 2; if (key_len == 128 / 8) { aes->K3R = endian_swap(*((u32 *) in_key + 0)); aes->K2R = endian_swap(*((u32 *) in_key + 1)); aes->K1R = endian_swap(*((u32 *) in_key + 2)); aes->K0R = endian_swap(*((u32 *) in_key + 3)); } else if (key_len == 192 / 8) { aes->K5R = endian_swap(*((u32 *) in_key + 0)); aes->K4R = endian_swap(*((u32 *) in_key + 1)); aes->K3R = endian_swap(*((u32 *) in_key + 2)); aes->K2R = endian_swap(*((u32 *) in_key + 3)); aes->K1R = endian_swap(*((u32 *) in_key + 4)); aes->K0R = endian_swap(*((u32 *) in_key + 5)); } else if (key_len == 256 / 8) { aes->K7R = endian_swap(*((u32 *) in_key + 0)); aes->K6R = endian_swap(*((u32 *) in_key + 1)); aes->K5R = endian_swap(*((u32 *) in_key + 2)); aes->K4R = endian_swap(*((u32 *) in_key + 3)); aes->K3R = endian_swap(*((u32 *) in_key + 4)); aes->K2R = endian_swap(*((u32 *) in_key + 5)); aes->K1R = endian_swap(*((u32 *) in_key + 6)); aes->K0R = endian_swap(*((u32 *) in_key + 7)); } else { printk (KERN_ERR "[%s %s %d]: Invalid key_len : %d\n", __FILE__, __func__, __LINE__, key_len); CRTCL_SECT_END; return;// -EINVAL; } /* let HW pre-process DEcryption key in any case (even if ENcryption is used). Key Valid (KV) bit is then only checked in decryption routine! */ aes->controlr.PNK = 1; #ifdef CONFIG_CRYPTO_DEV_AMAZON_S_DMA while (aes->controlr.BUS) { // this will not take long } #endif aes->controlr.E_D = !encdec; //encryption aes->controlr.O = mode; //0 ECB 1 CBC 2 OFB 3 CFB 4 CTR #ifdef CONFIG_CRYPTO_DEV_AMAZON_S_DMA aes->controlr.KRE = 1; aes->controlr.GO = 1; #endif //aes->controlr.F = 128; //default; only for CFB and OFB modes; change only for customer-specific apps if (mode > 0) { aes->IV3R = endian_swap(*(u32 *) iv_arg); aes->IV2R = endian_swap(*((u32 *) iv_arg + 1)); aes->IV1R = endian_swap(*((u32 *) iv_arg + 2)); aes->IV0R = endian_swap(*((u32 *) iv_arg + 3)); }; { #ifndef CONFIG_CRYPTO_DEV_AMAZON_S_DMA int i = 0; int byte_cnt = nbytes; i = 0; while (byte_cnt >= 16) { aes->ID3R = *((u32 *) in_arg + (i * 4) + 0); aes->ID2R = *((u32 *) in_arg + (i * 4) + 1); aes->ID1R = *((u32 *) in_arg + (i * 4) + 2); aes->ID0R = *((u32 *) in_arg + (i * 4) + 3); /* start crypto */ while (aes->controlr.BUS) { // this will not take long } *((volatile u32 *) out_arg + (i * 4) + 0) = aes->OD3R; *((volatile u32 *) out_arg + (i * 4) + 1) = aes->OD2R; *((volatile u32 *) out_arg + (i * 4) + 2) = aes->OD1R; *((volatile u32 *) out_arg + (i * 4) + 3) = aes->OD0R; i++; byte_cnt -= 16; } #else // dma //volatile struct deu_dma_t *dma = (struct deu_dma_t *) DMA_CON; struct dma_device_info *dma_device = ifx_deu[0].dma_device; _ifx_deu_device *pDev = ifx_deu; int wlen = 0; u32 timeout = 0; u8 *out_dma = NULL; pDev->len = nbytes; pDev->packet_size = nbytes; pDev->src = (u8 *)in_arg; pDev->dst = out_arg; pDev->dst_count = 0; //printk("aes->controlr = %08x\n", *AMAZON_S_AES_CON); //dma->controlr.ALGO = 1; //AES *AMAZON_S_CON = 0x00000211; //burst size=4 words; algo=AES; enable dma wlen = dma_device_write (dma_device, (u8 *)in_arg, nbytes, NULL); if (wlen != nbytes) { CRTCL_SECT_END; printk (KERN_ERR "[%s %s %d]: dma_device_write fail!\n", __FILE__, __func__, __LINE__); return; // -EINVAL; } #if 0 #define CNT_TEST 10 { volatile int cnt; for (cnt = 0; cnt < CNT_TEST; cnt++) udelay(10); } #endif while(((*AMAZON_S_CON & 0x00000080) == 0x00000080)){ // this will not take long }; while (aes->controlr.BUS) { // this will not take long } // polling DMA rx channel while ((pDev->recv_count = dma_device_read (dma_device, &out_dma, NULL)) == 0) { timeout++; if (timeout >= 333000) { deu_dma_release (); CRTCL_SECT_END; printk (KERN_ERR "[%s %s %d]: timeout!!\n", __FILE__, __func__, __LINE__); return; // -EINVAL; } } #if 0 for (cnt = 0; cnt < CNT_TEST; cnt++) udelay(10); #endif while(((*AMAZON_S_CON & 0x00000080) == 0x00000080)){ // this will not take long }; while (aes->controlr.BUS) { // this will not take long } memcpy (out_arg, out_dma, nbytes); #endif // dma } //tc.chen : copy iv_arg back if (mode > 0) { *((u32 *) iv_arg) = aes->IV3R; *((u32 *) iv_arg + 1) = aes->IV2R; *((u32 *) iv_arg + 2) = aes->IV1R; *((u32 *) iv_arg + 3) = aes->IV0R; *((u32 *) iv_arg) = endian_swap(*((u32 *) iv_arg)); *((u32 *) iv_arg + 1) = endian_swap(*((u32 *) iv_arg + 1)); *((u32 *) iv_arg + 2) = endian_swap(*((u32 *) iv_arg + 2)); *((u32 *) iv_arg + 3) = endian_swap(*((u32 *) iv_arg + 3)); } CRTCL_SECT_END; } #ifdef CONFIG_CRYPTO_DEV_AMAZON_S_DMA static void amazon_s_deu_aes (void *ctx_arg, u8 * out_arg, const u8 * in_arg, u8 * iv_arg, u32 nbytes, int encdec, int mode) { u32 remain = nbytes; u32 inc; while (remain > 0) { if (remain >= DEU_MAX_PACKET_SIZE) { inc = DEU_MAX_PACKET_SIZE; } else { inc = remain; } remain -= inc; amazon_s_deu_aes_core(ctx_arg, out_arg, in_arg, iv_arg, inc, encdec, mode); out_arg += inc; in_arg += inc; } } #endif //definitions from linux/include/crypto.h: //#define CRYPTO_TFM_MODE_ECB 0x00000001 //#define CRYPTO_TFM_MODE_CBC 0x00000002 //#define CRYPTO_TFM_MODE_CFB 0x00000004 //#define CRYPTO_TFM_MODE_CTR 0x00000008 //#define CRYPTO_TFM_MODE_OFB 0x00000010 // not even defined //but hardware definition: 0 ECB 1 CBC 2 OFB 3 CFB 4 CTR /*! \fn static void amazon_s_deu_aes_ecb (void *ctx, uint8_t *dst, const uint8_t *src, uint8_t *iv, size_t nbytes, int encdec, int inplace) \ingroup AMAZON_S_DEU_FUNCTIONS \brief sets AES hardware to ECB mode \param ctx crypto algo context \param dst output bytestream \param src input bytestream \param iv initialization vector \param nbytes length of bytestream \param encdec 1 for encrypt; 0 for decrypt \param inplace not used */ static void amazon_s_deu_aes_ecb (void *ctx, uint8_t *dst, const uint8_t *src, uint8_t *iv, size_t nbytes, int encdec, int inplace) { amazon_s_deu_aes (ctx, dst, src, NULL, nbytes, encdec, 0); } /*! \fn static void amazon_s_deu_aes_cbc (void *ctx, uint8_t *dst, const uint8_t *src, uint8_t *iv, size_t nbytes, int encdec, int inplace) \ingroup AMAZON_S_DEU_FUNCTIONS \brief sets AES hardware to CBC mode \param ctx crypto algo context \param dst output bytestream \param src input bytestream \param iv initialization vector \param nbytes length of bytestream \param encdec 1 for encrypt; 0 for decrypt \param inplace not used */ static void amazon_s_deu_aes_cbc (void *ctx, uint8_t *dst, const uint8_t *src, uint8_t *iv, size_t nbytes, int encdec, int inplace) { amazon_s_deu_aes (ctx, dst, src, iv, nbytes, encdec, 1); } #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) /*! \fn static void amazon_s_deu_aes_ofb (void *ctx, uint8_t *dst, const uint8_t *src, uint8_t *iv, size_t nbytes, int encdec, int inplace) \ingroup AMAZON_S_DEU_FUNCTIONS \brief sets AES hardware to OFB mode \param ctx crypto algo context \param dst output bytestream \param src input bytestream \param iv initialization vector \param nbytes length of bytestream \param encdec 1 for encrypt; 0 for decrypt \param inplace not used */ static void amazon_s_deu_aes_ofb (void *ctx, uint8_t *dst, const uint8_t *src, uint8_t *iv, size_t nbytes, int encdec, int inplace) { amazon_s_deu_aes (ctx, dst, src, iv, nbytes, encdec, 2); } /*! \fn static void amazon_s_deu_aes_cfb (void *ctx, uint8_t *dst, const uint8_t *src, uint8_t *iv, size_t nbytes, int encdec, int inplace) \ingroup AMAZON_S_DEU_FUNCTIONS \brief sets AES hardware to CFB mode \param ctx crypto algo context \param dst output bytestream \param src input bytestream \param iv initialization vector \param nbytes length of bytestream \param encdec 1 for encrypt; 0 for decrypt \param inplace not used */ static void amazon_s_deu_aes_cfb (void *ctx, uint8_t *dst, const uint8_t *src, uint8_t *iv, size_t nbytes, int encdec, int inplace) { amazon_s_deu_aes (ctx, dst, src, iv, nbytes, encdec, 3); } #endif // (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) /*! \fn static void amazon_s_deu_aes_ctr (void *ctx, uint8_t *dst, const uint8_t *src, uint8_t *iv, size_t nbytes, int encdec, int inplace) \ingroup AMAZON_S_DEU_FUNCTIONS \brief sets AES hardware to CTR mode \param ctx crypto algo context \param dst output bytestream \param src input bytestream \param iv initialization vector \param nbytes length of bytestream \param encdec 1 for encrypt; 0 for decrypt \param inplace not used */ static void amazon_s_deu_aes_ctr (void *ctx, uint8_t *dst, const uint8_t *src, uint8_t *iv, size_t nbytes, int encdec, int inplace) { amazon_s_deu_aes (ctx, dst, src, iv, nbytes, encdec, 4); } /*! \fn static void aes_encrypt (struct crypto_tfm *tfm, uint8_t *out, const uint8_t *in) \ingroup AMAZON_S_DEU_FUNCTIONS \brief encrypt AES_BLOCK_SIZE of data \param tfm linux crypto algo transform \param out output bytestream \param in input bytestream */ static void aes_encrypt (struct crypto_tfm *tfm, uint8_t *out, const uint8_t *in) { struct aes_ctx *ctx = crypto_tfm_ctx(tfm); amazon_s_deu_aes (ctx, out, in, NULL, AES_BLOCK_SIZE, CRYPTO_DIR_ENCRYPT, 0); } /*! \fn static void aes_decrypt (struct crypto_tfm *tfm, uint8_t *out, const uint8_t *in) \ingroup AMAZON_S_DEU_FUNCTIONS \brief decrypt AES_BLOCK_SIZE of data \param tfm linux crypto algo transform \param out output bytestream \param in input bytestream */ static void aes_decrypt (struct crypto_tfm *tfm, uint8_t *out, const uint8_t *in) { struct aes_ctx *ctx = crypto_tfm_ctx(tfm); amazon_s_deu_aes (ctx, out, in, NULL, AES_BLOCK_SIZE, CRYPTO_DIR_DECRYPT, 0); } /*! \fn static int aes_chip_init (void) \ingroup AMAZON_S_DEU_FUNCTIONS \brief initialize AES hardware */ static int aes_chip_init (void) { volatile struct aes_t *aes = (struct aes_t *) AES_START; #ifndef CONFIG_CRYPTO_DEV_AMAZON_S_DMA // start crypto engine with write to ILR aes->controlr.SM = 1; aes->controlr.ARS = 1; #else aes->controlr.SM = 1; aes->controlr.ARS = 0; // 0 for dma // needed for dma mode aes->controlr.NDC = 1; asm("sync"); aes->controlr.ENDI = 0; #endif return 0; } static struct crypto_alg ifxdeu_aes_alg = { .cra_name = "aes", .cra_driver_name = "ifxdeu-aes", .cra_flags = CRYPTO_ALG_TYPE_CIPHER, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aes_ctx), .cra_module = THIS_MODULE, .cra_list = LIST_HEAD_INIT(ifxdeu_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, .cia_decrypt = aes_decrypt, } } }; /*! \fn static int ecb_aes_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) \ingroup AMAZON_S_DEU_FUNCTIONS \brief ECB AES encrypt using linux crypto blkcipher \param desc blkcipher descriptor \param dst output scatterlist \param src input scatterlist \param nbytes data size in bytes */ static int ecb_aes_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) { struct aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); struct blkcipher_walk walk; int err; blkcipher_walk_init(&walk, dst, src, nbytes); err = blkcipher_walk_virt(desc, &walk); while ((nbytes = walk.nbytes)) { nbytes -= (nbytes % AES_BLOCK_SIZE); amazon_s_deu_aes_ecb(ctx, walk.dst.virt.addr, walk.src.virt.addr, NULL, nbytes, CRYPTO_DIR_ENCRYPT, 0); nbytes &= AES_BLOCK_SIZE - 1; err = blkcipher_walk_done(desc, &walk, nbytes); } return err; } /*! \fn static int ecb_aes_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) \ingroup AMAZON_S_DEU_FUNCTIONS \brief ECB AES decrypt using linux crypto blkcipher \param desc blkcipher descriptor \param dst output scatterlist \param src input scatterlist \param nbytes data size in bytes */ static int ecb_aes_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) { struct aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); struct blkcipher_walk walk; int err; blkcipher_walk_init(&walk, dst, src, nbytes); err = blkcipher_walk_virt(desc, &walk); while ((nbytes = walk.nbytes)) { nbytes -= (nbytes % AES_BLOCK_SIZE); amazon_s_deu_aes_ecb(ctx, walk.dst.virt.addr, walk.src.virt.addr, NULL, nbytes, CRYPTO_DIR_DECRYPT, 0); nbytes &= AES_BLOCK_SIZE - 1; err = blkcipher_walk_done(desc, &walk, nbytes); } return err; } static struct crypto_alg ifxdeu_ecb_aes_alg = { .cra_name = "ecb(aes)", .cra_driver_name = "ifxdeu-ecb(aes)", .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aes_ctx), .cra_type = &crypto_blkcipher_type, .cra_module = THIS_MODULE, .cra_list = LIST_HEAD_INIT(ifxdeu_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, } } }; /*! \fn static int cbc_aes_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) \ingroup AMAZON_S_DEU_FUNCTIONS \brief CBC AES encrypt using linux crypto blkcipher \param desc blkcipher descriptor \param dst output scatterlist \param src input scatterlist \param nbytes data size in bytes */ static int cbc_aes_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) { struct aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); struct blkcipher_walk walk; int err; blkcipher_walk_init(&walk, dst, src, nbytes); err = blkcipher_walk_virt(desc, &walk); while ((nbytes = walk.nbytes)) { u8 *iv = walk.iv; nbytes -= (nbytes % AES_BLOCK_SIZE); amazon_s_deu_aes_cbc(ctx, walk.dst.virt.addr, walk.src.virt.addr, iv, nbytes, CRYPTO_DIR_ENCRYPT, 0); nbytes &= AES_BLOCK_SIZE - 1; err = blkcipher_walk_done(desc, &walk, nbytes); } return err; } /*! \fn static int cbc_aes_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) \ingroup AMAZON_S_DEU_FUNCTIONS \brief CBC AES decrypt using linux crypto blkcipher \param desc blkcipher descriptor \param dst output scatterlist \param src input scatterlist \param nbytes data size in bytes */ static int cbc_aes_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) { struct aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); struct blkcipher_walk walk; int err; blkcipher_walk_init(&walk, dst, src, nbytes); err = blkcipher_walk_virt(desc, &walk); while ((nbytes = walk.nbytes)) { u8 *iv = walk.iv; nbytes -= (nbytes % AES_BLOCK_SIZE); amazon_s_deu_aes_cbc(ctx, walk.dst.virt.addr, walk.src.virt.addr, iv, nbytes, CRYPTO_DIR_DECRYPT, 0); nbytes &= AES_BLOCK_SIZE - 1; err = blkcipher_walk_done(desc, &walk, nbytes); } return err; } static struct crypto_alg ifxdeu_cbc_aes_alg = { .cra_name = "cbc(aes)", .cra_driver_name = "ifxdeu-cbc(aes)", .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aes_ctx), .cra_type = &crypto_blkcipher_type, .cra_module = THIS_MODULE, .cra_list = LIST_HEAD_INIT(ifxdeu_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, } } }; /*! \fn static int ctr_basic_aes_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) \ingroup AMAZON_S_DEU_FUNCTIONS \brief Counter mode AES encrypt using linux crypto blkcipher \param desc blkcipher descriptor \param dst output scatterlist \param src input scatterlist \param nbytes data size in bytes */ static int ctr_basic_aes_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) { struct aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); struct blkcipher_walk walk; int err; blkcipher_walk_init(&walk, dst, src, nbytes); err = blkcipher_walk_virt(desc, &walk); while ((nbytes = walk.nbytes)) { u8 *iv = walk.iv; nbytes -= (nbytes % AES_BLOCK_SIZE); amazon_s_deu_aes_ctr(ctx, walk.dst.virt.addr, walk.src.virt.addr, iv, nbytes, CRYPTO_DIR_ENCRYPT, 0); nbytes &= AES_BLOCK_SIZE - 1; err = blkcipher_walk_done(desc, &walk, nbytes); } return err; } /*! \fn static int ctr_basic_aes_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) \ingroup AMAZON_S_DEU_FUNCTIONS \brief Counter mode AES decrypt using linux crypto blkcipher \param desc blkcipher descriptor \param dst output scatterlist \param src input scatterlist \param nbytes data size in bytes */ static int ctr_basic_aes_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) { struct aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); struct blkcipher_walk walk; int err; blkcipher_walk_init(&walk, dst, src, nbytes); err = blkcipher_walk_virt(desc, &walk); while ((nbytes = walk.nbytes)) { u8 *iv = walk.iv; nbytes -= (nbytes % AES_BLOCK_SIZE); amazon_s_deu_aes_ctr(ctx, walk.dst.virt.addr, walk.src.virt.addr, iv, nbytes, CRYPTO_DIR_DECRYPT, 0); nbytes &= AES_BLOCK_SIZE - 1; err = blkcipher_walk_done(desc, &walk, nbytes); } return err; } static struct crypto_alg ifxdeu_ctr_basic_aes_alg = { .cra_name = "ctr(aes)", .cra_driver_name = "ifxdeu-ctr(aes)", .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aes_ctx), .cra_type = &crypto_blkcipher_type, .cra_module = THIS_MODULE, .cra_list = LIST_HEAD_INIT(ifxdeu_ctr_basic_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 = ctr_basic_aes_encrypt, .decrypt = ctr_basic_aes_decrypt, } } }; /*! \fn static int ctr_rfc3686_aes_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) \ingroup AMAZON_S_DEU_FUNCTIONS \brief Counter mode AES (rfc3686) encrypt using linux crypto blkcipher \param desc blkcipher descriptor \param dst output scatterlist \param src input scatterlist \param nbytes data size in bytes */ static int ctr_rfc3686_aes_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) { struct aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); struct blkcipher_walk walk; int err; u8 rfc3686_iv[16]; blkcipher_walk_init(&walk, dst, src, nbytes); err = blkcipher_walk_virt(desc, &walk); /* set up counter block */ memcpy(rfc3686_iv, ctx->nonce, CTR_RFC3686_NONCE_SIZE); memcpy(rfc3686_iv + CTR_RFC3686_NONCE_SIZE, walk.iv, CTR_RFC3686_IV_SIZE); /* initialize counter portion of counter block */ *(__be32 *)(rfc3686_iv + CTR_RFC3686_NONCE_SIZE + CTR_RFC3686_IV_SIZE) = cpu_to_be32(1); while ((nbytes = walk.nbytes)) { nbytes -= (nbytes % AES_BLOCK_SIZE); amazon_s_deu_aes_ctr(ctx, walk.dst.virt.addr, walk.src.virt.addr, rfc3686_iv, nbytes, CRYPTO_DIR_ENCRYPT, 0); nbytes &= AES_BLOCK_SIZE - 1; err = blkcipher_walk_done(desc, &walk, nbytes); } return err; } /*! \fn static int ctr_rfc3686_aes_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) \ingroup AMAZON_S_DEU_FUNCTIONS \brief Counter mode AES (rfc3686) decrypt using linux crypto blkcipher \param desc blkcipher descriptor \param dst output scatterlist \param src input scatterlist \param nbytes data size in bytes */ static int ctr_rfc3686_aes_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) { struct aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); struct blkcipher_walk walk; int err; u8 rfc3686_iv[16]; blkcipher_walk_init(&walk, dst, src, nbytes); err = blkcipher_walk_virt(desc, &walk); /* set up counter block */ memcpy(rfc3686_iv, ctx->nonce, CTR_RFC3686_NONCE_SIZE); memcpy(rfc3686_iv + CTR_RFC3686_NONCE_SIZE, walk.iv, CTR_RFC3686_IV_SIZE); /* initialize counter portion of counter block */ *(__be32 *)(rfc3686_iv + CTR_RFC3686_NONCE_SIZE + CTR_RFC3686_IV_SIZE) = cpu_to_be32(1); while ((nbytes = walk.nbytes)) { nbytes -= (nbytes % AES_BLOCK_SIZE); amazon_s_deu_aes_ctr(ctx, walk.dst.virt.addr, walk.src.virt.addr, rfc3686_iv, nbytes, CRYPTO_DIR_DECRYPT, 0); nbytes &= AES_BLOCK_SIZE - 1; err = blkcipher_walk_done(desc, &walk, nbytes); } return err; } static struct crypto_alg ifxdeu_ctr_rfc3686_aes_alg = { .cra_name = "rfc3686(ctr(aes))", .cra_driver_name = "ifxdeu-ctr-rfc3686(aes)", .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aes_ctx), .cra_type = &crypto_blkcipher_type, .cra_module = THIS_MODULE, .cra_list = LIST_HEAD_INIT(ifxdeu_ctr_rfc3686_aes_alg.cra_list), .cra_u = { .blkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = CTR_RFC3686_MAX_KEY_SIZE, .ivsize = CTR_RFC3686_IV_SIZE, .setkey = ctr_rfc3686_aes_set_key, .encrypt = ctr_rfc3686_aes_encrypt, .decrypt = ctr_rfc3686_aes_decrypt, } } }; /*! \fn int __init ifxdeu_init_aes (void) \ingroup AMAZON_S_DEU_FUNCTIONS \brief initialize aes driver */ int __init ifxdeu_init_aes (void) { int ret; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) if (!disable_multiblock) { ifxdeu_aes_alg.cra_u.cipher.cia_max_nbytes = AES_BLOCK_SIZE; //(size_t)-1; ifxdeu_aes_alg.cra_u.cipher.cia_req_align = 16; ifxdeu_aes_alg.cra_u.cipher.cia_ecb = amazon_s_deu_aes_ecb; ifxdeu_aes_alg.cra_u.cipher.cia_cbc = amazon_s_deu_aes_cbc; ifxdeu_aes_alg.cra_u.cipher.cia_cfb = amazon_s_deu_aes_cfb; ifxdeu_aes_alg.cra_u.cipher.cia_ofb = amazon_s_deu_aes_ofb; } #endif if ((ret = crypto_register_alg(&ifxdeu_aes_alg))) goto aes_err; if ((ret = crypto_register_alg(&ifxdeu_ecb_aes_alg))) goto ecb_aes_err; if ((ret = crypto_register_alg(&ifxdeu_cbc_aes_alg))) goto cbc_aes_err; if ((ret = crypto_register_alg(&ifxdeu_ctr_basic_aes_alg))) goto ctr_basic_aes_err; if ((ret = crypto_register_alg(&ifxdeu_ctr_rfc3686_aes_alg))) goto ctr_rfc3686_aes_err; aes_chip_init (); CRTCL_SECT_INIT; printk (KERN_NOTICE "IFX DEU AES initialized%s%s.\n", disable_multiblock ? "" : " (multiblock)", disable_deudma ? "" : " (DMA)"); return ret; ctr_rfc3686_aes_err: crypto_unregister_alg(&ifxdeu_ctr_rfc3686_aes_alg); printk (KERN_ERR "IFX ctr_rfc3686_aes initialization failed!\n"); return ret; ctr_basic_aes_err: crypto_unregister_alg(&ifxdeu_ctr_basic_aes_alg); printk (KERN_ERR "IFX ctr_basic_aes initialization failed!\n"); return ret; cbc_aes_err: crypto_unregister_alg(&ifxdeu_cbc_aes_alg); printk (KERN_ERR "IFX cbc_aes initialization failed!\n"); return ret; ecb_aes_err: crypto_unregister_alg(&ifxdeu_ecb_aes_alg); printk (KERN_ERR "IFX aes initialization failed!\n"); return ret; aes_err: printk(KERN_ERR "IFX DEU AES initialization failed!\n"); return ret; } /*! \fn void __exit ifxdeu_fini_aes (void) \ingroup AMAZON_S_DEU_FUNCTIONS \brief unregister aes driver */ void __exit ifxdeu_fini_aes (void) { crypto_unregister_alg (&ifxdeu_aes_alg); crypto_unregister_alg (&ifxdeu_ecb_aes_alg); crypto_unregister_alg (&ifxdeu_cbc_aes_alg); crypto_unregister_alg (&ifxdeu_ctr_basic_aes_alg); crypto_unregister_alg (&ifxdeu_ctr_rfc3686_aes_alg); }