164b94ceaSJussi Kivilinna /* 264b94ceaSJussi Kivilinna * Glue Code for assembler optimized version of Blowfish 364b94ceaSJussi Kivilinna * 464b94ceaSJussi Kivilinna * Copyright (c) 2011 Jussi Kivilinna <jussi.kivilinna@mbnet.fi> 564b94ceaSJussi Kivilinna * 6a071d06eSJussi Kivilinna * CBC & ECB parts based on code (crypto/cbc.c,ecb.c) by: 7a071d06eSJussi Kivilinna * Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au> 8a071d06eSJussi Kivilinna * CTR part based on code (crypto/ctr.c) by: 9a071d06eSJussi Kivilinna * (C) Copyright IBM Corp. 2007 - Joy Latten <latten@us.ibm.com> 10a071d06eSJussi Kivilinna * 1164b94ceaSJussi Kivilinna * This program is free software; you can redistribute it and/or modify 1264b94ceaSJussi Kivilinna * it under the terms of the GNU General Public License as published by 1364b94ceaSJussi Kivilinna * the Free Software Foundation; either version 2 of the License, or 1464b94ceaSJussi Kivilinna * (at your option) any later version. 1564b94ceaSJussi Kivilinna * 1664b94ceaSJussi Kivilinna * This program is distributed in the hope that it will be useful, 1764b94ceaSJussi Kivilinna * but WITHOUT ANY WARRANTY; without even the implied warranty of 1864b94ceaSJussi Kivilinna * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1964b94ceaSJussi Kivilinna * GNU General Public License for more details. 2064b94ceaSJussi Kivilinna * 2164b94ceaSJussi Kivilinna * You should have received a copy of the GNU General Public License 2264b94ceaSJussi Kivilinna * along with this program; if not, write to the Free Software 2364b94ceaSJussi Kivilinna * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 2464b94ceaSJussi Kivilinna * USA 2564b94ceaSJussi Kivilinna * 2664b94ceaSJussi Kivilinna */ 2764b94ceaSJussi Kivilinna 28*4c58464bSJussi Kivilinna #include <asm/processor.h> 2964b94ceaSJussi Kivilinna #include <crypto/blowfish.h> 3064b94ceaSJussi Kivilinna #include <linux/crypto.h> 3164b94ceaSJussi Kivilinna #include <linux/init.h> 3264b94ceaSJussi Kivilinna #include <linux/module.h> 3364b94ceaSJussi Kivilinna #include <linux/types.h> 3464b94ceaSJussi Kivilinna #include <crypto/algapi.h> 3564b94ceaSJussi Kivilinna 3664b94ceaSJussi Kivilinna /* regular block cipher functions */ 3764b94ceaSJussi Kivilinna asmlinkage void __blowfish_enc_blk(struct bf_ctx *ctx, u8 *dst, const u8 *src, 3864b94ceaSJussi Kivilinna bool xor); 3964b94ceaSJussi Kivilinna asmlinkage void blowfish_dec_blk(struct bf_ctx *ctx, u8 *dst, const u8 *src); 4064b94ceaSJussi Kivilinna 4164b94ceaSJussi Kivilinna /* 4-way parallel cipher functions */ 4264b94ceaSJussi Kivilinna asmlinkage void __blowfish_enc_blk_4way(struct bf_ctx *ctx, u8 *dst, 4364b94ceaSJussi Kivilinna const u8 *src, bool xor); 4464b94ceaSJussi Kivilinna asmlinkage void blowfish_dec_blk_4way(struct bf_ctx *ctx, u8 *dst, 4564b94ceaSJussi Kivilinna const u8 *src); 4664b94ceaSJussi Kivilinna 4764b94ceaSJussi Kivilinna static inline void blowfish_enc_blk(struct bf_ctx *ctx, u8 *dst, const u8 *src) 4864b94ceaSJussi Kivilinna { 4964b94ceaSJussi Kivilinna __blowfish_enc_blk(ctx, dst, src, false); 5064b94ceaSJussi Kivilinna } 5164b94ceaSJussi Kivilinna 5264b94ceaSJussi Kivilinna static inline void blowfish_enc_blk_xor(struct bf_ctx *ctx, u8 *dst, 5364b94ceaSJussi Kivilinna const u8 *src) 5464b94ceaSJussi Kivilinna { 5564b94ceaSJussi Kivilinna __blowfish_enc_blk(ctx, dst, src, true); 5664b94ceaSJussi Kivilinna } 5764b94ceaSJussi Kivilinna 5864b94ceaSJussi Kivilinna static inline void blowfish_enc_blk_4way(struct bf_ctx *ctx, u8 *dst, 5964b94ceaSJussi Kivilinna const u8 *src) 6064b94ceaSJussi Kivilinna { 6164b94ceaSJussi Kivilinna __blowfish_enc_blk_4way(ctx, dst, src, false); 6264b94ceaSJussi Kivilinna } 6364b94ceaSJussi Kivilinna 6464b94ceaSJussi Kivilinna static inline void blowfish_enc_blk_xor_4way(struct bf_ctx *ctx, u8 *dst, 6564b94ceaSJussi Kivilinna const u8 *src) 6664b94ceaSJussi Kivilinna { 6764b94ceaSJussi Kivilinna __blowfish_enc_blk_4way(ctx, dst, src, true); 6864b94ceaSJussi Kivilinna } 6964b94ceaSJussi Kivilinna 7064b94ceaSJussi Kivilinna static void blowfish_encrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) 7164b94ceaSJussi Kivilinna { 7264b94ceaSJussi Kivilinna blowfish_enc_blk(crypto_tfm_ctx(tfm), dst, src); 7364b94ceaSJussi Kivilinna } 7464b94ceaSJussi Kivilinna 7564b94ceaSJussi Kivilinna static void blowfish_decrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) 7664b94ceaSJussi Kivilinna { 7764b94ceaSJussi Kivilinna blowfish_dec_blk(crypto_tfm_ctx(tfm), dst, src); 7864b94ceaSJussi Kivilinna } 7964b94ceaSJussi Kivilinna 8064b94ceaSJussi Kivilinna static struct crypto_alg bf_alg = { 8164b94ceaSJussi Kivilinna .cra_name = "blowfish", 8264b94ceaSJussi Kivilinna .cra_driver_name = "blowfish-asm", 8364b94ceaSJussi Kivilinna .cra_priority = 200, 8464b94ceaSJussi Kivilinna .cra_flags = CRYPTO_ALG_TYPE_CIPHER, 8564b94ceaSJussi Kivilinna .cra_blocksize = BF_BLOCK_SIZE, 8664b94ceaSJussi Kivilinna .cra_ctxsize = sizeof(struct bf_ctx), 8764b94ceaSJussi Kivilinna .cra_alignmask = 3, 8864b94ceaSJussi Kivilinna .cra_module = THIS_MODULE, 8964b94ceaSJussi Kivilinna .cra_list = LIST_HEAD_INIT(bf_alg.cra_list), 9064b94ceaSJussi Kivilinna .cra_u = { 9164b94ceaSJussi Kivilinna .cipher = { 9264b94ceaSJussi Kivilinna .cia_min_keysize = BF_MIN_KEY_SIZE, 9364b94ceaSJussi Kivilinna .cia_max_keysize = BF_MAX_KEY_SIZE, 9464b94ceaSJussi Kivilinna .cia_setkey = blowfish_setkey, 9564b94ceaSJussi Kivilinna .cia_encrypt = blowfish_encrypt, 9664b94ceaSJussi Kivilinna .cia_decrypt = blowfish_decrypt, 9764b94ceaSJussi Kivilinna } 9864b94ceaSJussi Kivilinna } 9964b94ceaSJussi Kivilinna }; 10064b94ceaSJussi Kivilinna 10164b94ceaSJussi Kivilinna static int ecb_crypt(struct blkcipher_desc *desc, struct blkcipher_walk *walk, 10264b94ceaSJussi Kivilinna void (*fn)(struct bf_ctx *, u8 *, const u8 *), 10364b94ceaSJussi Kivilinna void (*fn_4way)(struct bf_ctx *, u8 *, const u8 *)) 10464b94ceaSJussi Kivilinna { 10564b94ceaSJussi Kivilinna struct bf_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); 10664b94ceaSJussi Kivilinna unsigned int bsize = BF_BLOCK_SIZE; 10764b94ceaSJussi Kivilinna unsigned int nbytes; 10864b94ceaSJussi Kivilinna int err; 10964b94ceaSJussi Kivilinna 11064b94ceaSJussi Kivilinna err = blkcipher_walk_virt(desc, walk); 11164b94ceaSJussi Kivilinna 11264b94ceaSJussi Kivilinna while ((nbytes = walk->nbytes)) { 11364b94ceaSJussi Kivilinna u8 *wsrc = walk->src.virt.addr; 11464b94ceaSJussi Kivilinna u8 *wdst = walk->dst.virt.addr; 11564b94ceaSJussi Kivilinna 11664b94ceaSJussi Kivilinna /* Process four block batch */ 11764b94ceaSJussi Kivilinna if (nbytes >= bsize * 4) { 11864b94ceaSJussi Kivilinna do { 11964b94ceaSJussi Kivilinna fn_4way(ctx, wdst, wsrc); 12064b94ceaSJussi Kivilinna 12164b94ceaSJussi Kivilinna wsrc += bsize * 4; 12264b94ceaSJussi Kivilinna wdst += bsize * 4; 12364b94ceaSJussi Kivilinna nbytes -= bsize * 4; 12464b94ceaSJussi Kivilinna } while (nbytes >= bsize * 4); 12564b94ceaSJussi Kivilinna 12664b94ceaSJussi Kivilinna if (nbytes < bsize) 12764b94ceaSJussi Kivilinna goto done; 12864b94ceaSJussi Kivilinna } 12964b94ceaSJussi Kivilinna 13064b94ceaSJussi Kivilinna /* Handle leftovers */ 13164b94ceaSJussi Kivilinna do { 13264b94ceaSJussi Kivilinna fn(ctx, wdst, wsrc); 13364b94ceaSJussi Kivilinna 13464b94ceaSJussi Kivilinna wsrc += bsize; 13564b94ceaSJussi Kivilinna wdst += bsize; 13664b94ceaSJussi Kivilinna nbytes -= bsize; 13764b94ceaSJussi Kivilinna } while (nbytes >= bsize); 13864b94ceaSJussi Kivilinna 13964b94ceaSJussi Kivilinna done: 14064b94ceaSJussi Kivilinna err = blkcipher_walk_done(desc, walk, nbytes); 14164b94ceaSJussi Kivilinna } 14264b94ceaSJussi Kivilinna 14364b94ceaSJussi Kivilinna return err; 14464b94ceaSJussi Kivilinna } 14564b94ceaSJussi Kivilinna 14664b94ceaSJussi Kivilinna static int ecb_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, 14764b94ceaSJussi Kivilinna struct scatterlist *src, unsigned int nbytes) 14864b94ceaSJussi Kivilinna { 14964b94ceaSJussi Kivilinna struct blkcipher_walk walk; 15064b94ceaSJussi Kivilinna 15164b94ceaSJussi Kivilinna blkcipher_walk_init(&walk, dst, src, nbytes); 15264b94ceaSJussi Kivilinna return ecb_crypt(desc, &walk, blowfish_enc_blk, blowfish_enc_blk_4way); 15364b94ceaSJussi Kivilinna } 15464b94ceaSJussi Kivilinna 15564b94ceaSJussi Kivilinna static int ecb_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, 15664b94ceaSJussi Kivilinna struct scatterlist *src, unsigned int nbytes) 15764b94ceaSJussi Kivilinna { 15864b94ceaSJussi Kivilinna struct blkcipher_walk walk; 15964b94ceaSJussi Kivilinna 16064b94ceaSJussi Kivilinna blkcipher_walk_init(&walk, dst, src, nbytes); 16164b94ceaSJussi Kivilinna return ecb_crypt(desc, &walk, blowfish_dec_blk, blowfish_dec_blk_4way); 16264b94ceaSJussi Kivilinna } 16364b94ceaSJussi Kivilinna 16464b94ceaSJussi Kivilinna static struct crypto_alg blk_ecb_alg = { 16564b94ceaSJussi Kivilinna .cra_name = "ecb(blowfish)", 16664b94ceaSJussi Kivilinna .cra_driver_name = "ecb-blowfish-asm", 16764b94ceaSJussi Kivilinna .cra_priority = 300, 16864b94ceaSJussi Kivilinna .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, 16964b94ceaSJussi Kivilinna .cra_blocksize = BF_BLOCK_SIZE, 17064b94ceaSJussi Kivilinna .cra_ctxsize = sizeof(struct bf_ctx), 17164b94ceaSJussi Kivilinna .cra_alignmask = 0, 17264b94ceaSJussi Kivilinna .cra_type = &crypto_blkcipher_type, 17364b94ceaSJussi Kivilinna .cra_module = THIS_MODULE, 17464b94ceaSJussi Kivilinna .cra_list = LIST_HEAD_INIT(blk_ecb_alg.cra_list), 17564b94ceaSJussi Kivilinna .cra_u = { 17664b94ceaSJussi Kivilinna .blkcipher = { 17764b94ceaSJussi Kivilinna .min_keysize = BF_MIN_KEY_SIZE, 17864b94ceaSJussi Kivilinna .max_keysize = BF_MAX_KEY_SIZE, 17964b94ceaSJussi Kivilinna .setkey = blowfish_setkey, 18064b94ceaSJussi Kivilinna .encrypt = ecb_encrypt, 18164b94ceaSJussi Kivilinna .decrypt = ecb_decrypt, 18264b94ceaSJussi Kivilinna }, 18364b94ceaSJussi Kivilinna }, 18464b94ceaSJussi Kivilinna }; 18564b94ceaSJussi Kivilinna 18664b94ceaSJussi Kivilinna static unsigned int __cbc_encrypt(struct blkcipher_desc *desc, 18764b94ceaSJussi Kivilinna struct blkcipher_walk *walk) 18864b94ceaSJussi Kivilinna { 18964b94ceaSJussi Kivilinna struct bf_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); 19064b94ceaSJussi Kivilinna unsigned int bsize = BF_BLOCK_SIZE; 19164b94ceaSJussi Kivilinna unsigned int nbytes = walk->nbytes; 19264b94ceaSJussi Kivilinna u64 *src = (u64 *)walk->src.virt.addr; 19364b94ceaSJussi Kivilinna u64 *dst = (u64 *)walk->dst.virt.addr; 19464b94ceaSJussi Kivilinna u64 *iv = (u64 *)walk->iv; 19564b94ceaSJussi Kivilinna 19664b94ceaSJussi Kivilinna do { 19764b94ceaSJussi Kivilinna *dst = *src ^ *iv; 19864b94ceaSJussi Kivilinna blowfish_enc_blk(ctx, (u8 *)dst, (u8 *)dst); 19964b94ceaSJussi Kivilinna iv = dst; 20064b94ceaSJussi Kivilinna 20164b94ceaSJussi Kivilinna src += 1; 20264b94ceaSJussi Kivilinna dst += 1; 20364b94ceaSJussi Kivilinna nbytes -= bsize; 20464b94ceaSJussi Kivilinna } while (nbytes >= bsize); 20564b94ceaSJussi Kivilinna 20664b94ceaSJussi Kivilinna *(u64 *)walk->iv = *iv; 20764b94ceaSJussi Kivilinna return nbytes; 20864b94ceaSJussi Kivilinna } 20964b94ceaSJussi Kivilinna 21064b94ceaSJussi Kivilinna static int cbc_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, 21164b94ceaSJussi Kivilinna struct scatterlist *src, unsigned int nbytes) 21264b94ceaSJussi Kivilinna { 21364b94ceaSJussi Kivilinna struct blkcipher_walk walk; 21464b94ceaSJussi Kivilinna int err; 21564b94ceaSJussi Kivilinna 21664b94ceaSJussi Kivilinna blkcipher_walk_init(&walk, dst, src, nbytes); 21764b94ceaSJussi Kivilinna err = blkcipher_walk_virt(desc, &walk); 21864b94ceaSJussi Kivilinna 21964b94ceaSJussi Kivilinna while ((nbytes = walk.nbytes)) { 22064b94ceaSJussi Kivilinna nbytes = __cbc_encrypt(desc, &walk); 22164b94ceaSJussi Kivilinna err = blkcipher_walk_done(desc, &walk, nbytes); 22264b94ceaSJussi Kivilinna } 22364b94ceaSJussi Kivilinna 22464b94ceaSJussi Kivilinna return err; 22564b94ceaSJussi Kivilinna } 22664b94ceaSJussi Kivilinna 22764b94ceaSJussi Kivilinna static unsigned int __cbc_decrypt(struct blkcipher_desc *desc, 22864b94ceaSJussi Kivilinna struct blkcipher_walk *walk) 22964b94ceaSJussi Kivilinna { 23064b94ceaSJussi Kivilinna struct bf_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); 23164b94ceaSJussi Kivilinna unsigned int bsize = BF_BLOCK_SIZE; 23264b94ceaSJussi Kivilinna unsigned int nbytes = walk->nbytes; 23364b94ceaSJussi Kivilinna u64 *src = (u64 *)walk->src.virt.addr; 23464b94ceaSJussi Kivilinna u64 *dst = (u64 *)walk->dst.virt.addr; 23564b94ceaSJussi Kivilinna u64 ivs[4 - 1]; 23664b94ceaSJussi Kivilinna u64 last_iv; 23764b94ceaSJussi Kivilinna 23864b94ceaSJussi Kivilinna /* Start of the last block. */ 23964b94ceaSJussi Kivilinna src += nbytes / bsize - 1; 24064b94ceaSJussi Kivilinna dst += nbytes / bsize - 1; 24164b94ceaSJussi Kivilinna 24264b94ceaSJussi Kivilinna last_iv = *src; 24364b94ceaSJussi Kivilinna 24464b94ceaSJussi Kivilinna /* Process four block batch */ 24564b94ceaSJussi Kivilinna if (nbytes >= bsize * 4) { 24664b94ceaSJussi Kivilinna do { 24764b94ceaSJussi Kivilinna nbytes -= bsize * 4 - bsize; 24864b94ceaSJussi Kivilinna src -= 4 - 1; 24964b94ceaSJussi Kivilinna dst -= 4 - 1; 25064b94ceaSJussi Kivilinna 25164b94ceaSJussi Kivilinna ivs[0] = src[0]; 25264b94ceaSJussi Kivilinna ivs[1] = src[1]; 25364b94ceaSJussi Kivilinna ivs[2] = src[2]; 25464b94ceaSJussi Kivilinna 25564b94ceaSJussi Kivilinna blowfish_dec_blk_4way(ctx, (u8 *)dst, (u8 *)src); 25664b94ceaSJussi Kivilinna 25764b94ceaSJussi Kivilinna dst[1] ^= ivs[0]; 25864b94ceaSJussi Kivilinna dst[2] ^= ivs[1]; 25964b94ceaSJussi Kivilinna dst[3] ^= ivs[2]; 26064b94ceaSJussi Kivilinna 26164b94ceaSJussi Kivilinna nbytes -= bsize; 26264b94ceaSJussi Kivilinna if (nbytes < bsize) 26364b94ceaSJussi Kivilinna goto done; 26464b94ceaSJussi Kivilinna 26564b94ceaSJussi Kivilinna *dst ^= *(src - 1); 26664b94ceaSJussi Kivilinna src -= 1; 26764b94ceaSJussi Kivilinna dst -= 1; 26864b94ceaSJussi Kivilinna } while (nbytes >= bsize * 4); 26964b94ceaSJussi Kivilinna 27064b94ceaSJussi Kivilinna if (nbytes < bsize) 27164b94ceaSJussi Kivilinna goto done; 27264b94ceaSJussi Kivilinna } 27364b94ceaSJussi Kivilinna 27464b94ceaSJussi Kivilinna /* Handle leftovers */ 27564b94ceaSJussi Kivilinna for (;;) { 27664b94ceaSJussi Kivilinna blowfish_dec_blk(ctx, (u8 *)dst, (u8 *)src); 27764b94ceaSJussi Kivilinna 27864b94ceaSJussi Kivilinna nbytes -= bsize; 27964b94ceaSJussi Kivilinna if (nbytes < bsize) 28064b94ceaSJussi Kivilinna break; 28164b94ceaSJussi Kivilinna 28264b94ceaSJussi Kivilinna *dst ^= *(src - 1); 28364b94ceaSJussi Kivilinna src -= 1; 28464b94ceaSJussi Kivilinna dst -= 1; 28564b94ceaSJussi Kivilinna } 28664b94ceaSJussi Kivilinna 28764b94ceaSJussi Kivilinna done: 28864b94ceaSJussi Kivilinna *dst ^= *(u64 *)walk->iv; 28964b94ceaSJussi Kivilinna *(u64 *)walk->iv = last_iv; 29064b94ceaSJussi Kivilinna 29164b94ceaSJussi Kivilinna return nbytes; 29264b94ceaSJussi Kivilinna } 29364b94ceaSJussi Kivilinna 29464b94ceaSJussi Kivilinna static int cbc_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, 29564b94ceaSJussi Kivilinna struct scatterlist *src, unsigned int nbytes) 29664b94ceaSJussi Kivilinna { 29764b94ceaSJussi Kivilinna struct blkcipher_walk walk; 29864b94ceaSJussi Kivilinna int err; 29964b94ceaSJussi Kivilinna 30064b94ceaSJussi Kivilinna blkcipher_walk_init(&walk, dst, src, nbytes); 30164b94ceaSJussi Kivilinna err = blkcipher_walk_virt(desc, &walk); 30264b94ceaSJussi Kivilinna 30364b94ceaSJussi Kivilinna while ((nbytes = walk.nbytes)) { 30464b94ceaSJussi Kivilinna nbytes = __cbc_decrypt(desc, &walk); 30564b94ceaSJussi Kivilinna err = blkcipher_walk_done(desc, &walk, nbytes); 30664b94ceaSJussi Kivilinna } 30764b94ceaSJussi Kivilinna 30864b94ceaSJussi Kivilinna return err; 30964b94ceaSJussi Kivilinna } 31064b94ceaSJussi Kivilinna 31164b94ceaSJussi Kivilinna static struct crypto_alg blk_cbc_alg = { 31264b94ceaSJussi Kivilinna .cra_name = "cbc(blowfish)", 31364b94ceaSJussi Kivilinna .cra_driver_name = "cbc-blowfish-asm", 31464b94ceaSJussi Kivilinna .cra_priority = 300, 31564b94ceaSJussi Kivilinna .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, 31664b94ceaSJussi Kivilinna .cra_blocksize = BF_BLOCK_SIZE, 31764b94ceaSJussi Kivilinna .cra_ctxsize = sizeof(struct bf_ctx), 31864b94ceaSJussi Kivilinna .cra_alignmask = 0, 31964b94ceaSJussi Kivilinna .cra_type = &crypto_blkcipher_type, 32064b94ceaSJussi Kivilinna .cra_module = THIS_MODULE, 32164b94ceaSJussi Kivilinna .cra_list = LIST_HEAD_INIT(blk_cbc_alg.cra_list), 32264b94ceaSJussi Kivilinna .cra_u = { 32364b94ceaSJussi Kivilinna .blkcipher = { 32464b94ceaSJussi Kivilinna .min_keysize = BF_MIN_KEY_SIZE, 32564b94ceaSJussi Kivilinna .max_keysize = BF_MAX_KEY_SIZE, 32664b94ceaSJussi Kivilinna .ivsize = BF_BLOCK_SIZE, 32764b94ceaSJussi Kivilinna .setkey = blowfish_setkey, 32864b94ceaSJussi Kivilinna .encrypt = cbc_encrypt, 32964b94ceaSJussi Kivilinna .decrypt = cbc_decrypt, 33064b94ceaSJussi Kivilinna }, 33164b94ceaSJussi Kivilinna }, 33264b94ceaSJussi Kivilinna }; 33364b94ceaSJussi Kivilinna 33464b94ceaSJussi Kivilinna static void ctr_crypt_final(struct bf_ctx *ctx, struct blkcipher_walk *walk) 33564b94ceaSJussi Kivilinna { 33664b94ceaSJussi Kivilinna u8 *ctrblk = walk->iv; 33764b94ceaSJussi Kivilinna u8 keystream[BF_BLOCK_SIZE]; 33864b94ceaSJussi Kivilinna u8 *src = walk->src.virt.addr; 33964b94ceaSJussi Kivilinna u8 *dst = walk->dst.virt.addr; 34064b94ceaSJussi Kivilinna unsigned int nbytes = walk->nbytes; 34164b94ceaSJussi Kivilinna 34264b94ceaSJussi Kivilinna blowfish_enc_blk(ctx, keystream, ctrblk); 34364b94ceaSJussi Kivilinna crypto_xor(keystream, src, nbytes); 34464b94ceaSJussi Kivilinna memcpy(dst, keystream, nbytes); 34564b94ceaSJussi Kivilinna 34664b94ceaSJussi Kivilinna crypto_inc(ctrblk, BF_BLOCK_SIZE); 34764b94ceaSJussi Kivilinna } 34864b94ceaSJussi Kivilinna 34964b94ceaSJussi Kivilinna static unsigned int __ctr_crypt(struct blkcipher_desc *desc, 35064b94ceaSJussi Kivilinna struct blkcipher_walk *walk) 35164b94ceaSJussi Kivilinna { 35264b94ceaSJussi Kivilinna struct bf_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); 35364b94ceaSJussi Kivilinna unsigned int bsize = BF_BLOCK_SIZE; 35464b94ceaSJussi Kivilinna unsigned int nbytes = walk->nbytes; 35564b94ceaSJussi Kivilinna u64 *src = (u64 *)walk->src.virt.addr; 35664b94ceaSJussi Kivilinna u64 *dst = (u64 *)walk->dst.virt.addr; 35764b94ceaSJussi Kivilinna u64 ctrblk = be64_to_cpu(*(__be64 *)walk->iv); 35864b94ceaSJussi Kivilinna __be64 ctrblocks[4]; 35964b94ceaSJussi Kivilinna 36064b94ceaSJussi Kivilinna /* Process four block batch */ 36164b94ceaSJussi Kivilinna if (nbytes >= bsize * 4) { 36264b94ceaSJussi Kivilinna do { 36364b94ceaSJussi Kivilinna if (dst != src) { 36464b94ceaSJussi Kivilinna dst[0] = src[0]; 36564b94ceaSJussi Kivilinna dst[1] = src[1]; 36664b94ceaSJussi Kivilinna dst[2] = src[2]; 36764b94ceaSJussi Kivilinna dst[3] = src[3]; 36864b94ceaSJussi Kivilinna } 36964b94ceaSJussi Kivilinna 37064b94ceaSJussi Kivilinna /* create ctrblks for parallel encrypt */ 37164b94ceaSJussi Kivilinna ctrblocks[0] = cpu_to_be64(ctrblk++); 37264b94ceaSJussi Kivilinna ctrblocks[1] = cpu_to_be64(ctrblk++); 37364b94ceaSJussi Kivilinna ctrblocks[2] = cpu_to_be64(ctrblk++); 37464b94ceaSJussi Kivilinna ctrblocks[3] = cpu_to_be64(ctrblk++); 37564b94ceaSJussi Kivilinna 37664b94ceaSJussi Kivilinna blowfish_enc_blk_xor_4way(ctx, (u8 *)dst, 37764b94ceaSJussi Kivilinna (u8 *)ctrblocks); 37864b94ceaSJussi Kivilinna 37964b94ceaSJussi Kivilinna src += 4; 38064b94ceaSJussi Kivilinna dst += 4; 38164b94ceaSJussi Kivilinna } while ((nbytes -= bsize * 4) >= bsize * 4); 38264b94ceaSJussi Kivilinna 38364b94ceaSJussi Kivilinna if (nbytes < bsize) 38464b94ceaSJussi Kivilinna goto done; 38564b94ceaSJussi Kivilinna } 38664b94ceaSJussi Kivilinna 38764b94ceaSJussi Kivilinna /* Handle leftovers */ 38864b94ceaSJussi Kivilinna do { 38964b94ceaSJussi Kivilinna if (dst != src) 39064b94ceaSJussi Kivilinna *dst = *src; 39164b94ceaSJussi Kivilinna 39264b94ceaSJussi Kivilinna ctrblocks[0] = cpu_to_be64(ctrblk++); 39364b94ceaSJussi Kivilinna 39464b94ceaSJussi Kivilinna blowfish_enc_blk_xor(ctx, (u8 *)dst, (u8 *)ctrblocks); 39564b94ceaSJussi Kivilinna 39664b94ceaSJussi Kivilinna src += 1; 39764b94ceaSJussi Kivilinna dst += 1; 39864b94ceaSJussi Kivilinna } while ((nbytes -= bsize) >= bsize); 39964b94ceaSJussi Kivilinna 40064b94ceaSJussi Kivilinna done: 40164b94ceaSJussi Kivilinna *(__be64 *)walk->iv = cpu_to_be64(ctrblk); 40264b94ceaSJussi Kivilinna return nbytes; 40364b94ceaSJussi Kivilinna } 40464b94ceaSJussi Kivilinna 40564b94ceaSJussi Kivilinna static int ctr_crypt(struct blkcipher_desc *desc, struct scatterlist *dst, 40664b94ceaSJussi Kivilinna struct scatterlist *src, unsigned int nbytes) 40764b94ceaSJussi Kivilinna { 40864b94ceaSJussi Kivilinna struct blkcipher_walk walk; 40964b94ceaSJussi Kivilinna int err; 41064b94ceaSJussi Kivilinna 41164b94ceaSJussi Kivilinna blkcipher_walk_init(&walk, dst, src, nbytes); 41264b94ceaSJussi Kivilinna err = blkcipher_walk_virt_block(desc, &walk, BF_BLOCK_SIZE); 41364b94ceaSJussi Kivilinna 41464b94ceaSJussi Kivilinna while ((nbytes = walk.nbytes) >= BF_BLOCK_SIZE) { 41564b94ceaSJussi Kivilinna nbytes = __ctr_crypt(desc, &walk); 41664b94ceaSJussi Kivilinna err = blkcipher_walk_done(desc, &walk, nbytes); 41764b94ceaSJussi Kivilinna } 41864b94ceaSJussi Kivilinna 41964b94ceaSJussi Kivilinna if (walk.nbytes) { 42064b94ceaSJussi Kivilinna ctr_crypt_final(crypto_blkcipher_ctx(desc->tfm), &walk); 42164b94ceaSJussi Kivilinna err = blkcipher_walk_done(desc, &walk, 0); 42264b94ceaSJussi Kivilinna } 42364b94ceaSJussi Kivilinna 42464b94ceaSJussi Kivilinna return err; 42564b94ceaSJussi Kivilinna } 42664b94ceaSJussi Kivilinna 42764b94ceaSJussi Kivilinna static struct crypto_alg blk_ctr_alg = { 42864b94ceaSJussi Kivilinna .cra_name = "ctr(blowfish)", 42964b94ceaSJussi Kivilinna .cra_driver_name = "ctr-blowfish-asm", 43064b94ceaSJussi Kivilinna .cra_priority = 300, 43164b94ceaSJussi Kivilinna .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, 432a516ebafSJussi Kivilinna .cra_blocksize = 1, 43364b94ceaSJussi Kivilinna .cra_ctxsize = sizeof(struct bf_ctx), 43464b94ceaSJussi Kivilinna .cra_alignmask = 0, 43564b94ceaSJussi Kivilinna .cra_type = &crypto_blkcipher_type, 43664b94ceaSJussi Kivilinna .cra_module = THIS_MODULE, 43764b94ceaSJussi Kivilinna .cra_list = LIST_HEAD_INIT(blk_ctr_alg.cra_list), 43864b94ceaSJussi Kivilinna .cra_u = { 43964b94ceaSJussi Kivilinna .blkcipher = { 44064b94ceaSJussi Kivilinna .min_keysize = BF_MIN_KEY_SIZE, 44164b94ceaSJussi Kivilinna .max_keysize = BF_MAX_KEY_SIZE, 44264b94ceaSJussi Kivilinna .ivsize = BF_BLOCK_SIZE, 44364b94ceaSJussi Kivilinna .setkey = blowfish_setkey, 44464b94ceaSJussi Kivilinna .encrypt = ctr_crypt, 44564b94ceaSJussi Kivilinna .decrypt = ctr_crypt, 44664b94ceaSJussi Kivilinna }, 44764b94ceaSJussi Kivilinna }, 44864b94ceaSJussi Kivilinna }; 44964b94ceaSJussi Kivilinna 450*4c58464bSJussi Kivilinna static bool is_blacklisted_cpu(void) 451*4c58464bSJussi Kivilinna { 452*4c58464bSJussi Kivilinna if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) 453*4c58464bSJussi Kivilinna return false; 454*4c58464bSJussi Kivilinna 455*4c58464bSJussi Kivilinna if (boot_cpu_data.x86 == 0x0f) { 456*4c58464bSJussi Kivilinna /* 457*4c58464bSJussi Kivilinna * On Pentium 4, blowfish-x86_64 is slower than generic C 458*4c58464bSJussi Kivilinna * implementation because use of 64bit rotates (which are really 459*4c58464bSJussi Kivilinna * slow on P4). Therefore blacklist P4s. 460*4c58464bSJussi Kivilinna */ 461*4c58464bSJussi Kivilinna return true; 462*4c58464bSJussi Kivilinna } 463*4c58464bSJussi Kivilinna 464*4c58464bSJussi Kivilinna return false; 465*4c58464bSJussi Kivilinna } 466*4c58464bSJussi Kivilinna 467*4c58464bSJussi Kivilinna static int force; 468*4c58464bSJussi Kivilinna module_param(force, int, 0); 469*4c58464bSJussi Kivilinna MODULE_PARM_DESC(force, "Force module load, ignore CPU blacklist"); 470*4c58464bSJussi Kivilinna 47164b94ceaSJussi Kivilinna static int __init init(void) 47264b94ceaSJussi Kivilinna { 47364b94ceaSJussi Kivilinna int err; 47464b94ceaSJussi Kivilinna 475*4c58464bSJussi Kivilinna if (!force && is_blacklisted_cpu()) { 476*4c58464bSJussi Kivilinna printk(KERN_INFO 477*4c58464bSJussi Kivilinna "blowfish-x86_64: performance on this CPU " 478*4c58464bSJussi Kivilinna "would be suboptimal: disabling " 479*4c58464bSJussi Kivilinna "blowfish-x86_64.\n"); 480*4c58464bSJussi Kivilinna return -ENODEV; 481*4c58464bSJussi Kivilinna } 482*4c58464bSJussi Kivilinna 48364b94ceaSJussi Kivilinna err = crypto_register_alg(&bf_alg); 48464b94ceaSJussi Kivilinna if (err) 48564b94ceaSJussi Kivilinna goto bf_err; 48664b94ceaSJussi Kivilinna err = crypto_register_alg(&blk_ecb_alg); 48764b94ceaSJussi Kivilinna if (err) 48864b94ceaSJussi Kivilinna goto ecb_err; 48964b94ceaSJussi Kivilinna err = crypto_register_alg(&blk_cbc_alg); 49064b94ceaSJussi Kivilinna if (err) 49164b94ceaSJussi Kivilinna goto cbc_err; 49264b94ceaSJussi Kivilinna err = crypto_register_alg(&blk_ctr_alg); 49364b94ceaSJussi Kivilinna if (err) 49464b94ceaSJussi Kivilinna goto ctr_err; 49564b94ceaSJussi Kivilinna 49664b94ceaSJussi Kivilinna return 0; 49764b94ceaSJussi Kivilinna 49864b94ceaSJussi Kivilinna ctr_err: 49964b94ceaSJussi Kivilinna crypto_unregister_alg(&blk_cbc_alg); 50064b94ceaSJussi Kivilinna cbc_err: 50164b94ceaSJussi Kivilinna crypto_unregister_alg(&blk_ecb_alg); 50264b94ceaSJussi Kivilinna ecb_err: 50364b94ceaSJussi Kivilinna crypto_unregister_alg(&bf_alg); 50464b94ceaSJussi Kivilinna bf_err: 50564b94ceaSJussi Kivilinna return err; 50664b94ceaSJussi Kivilinna } 50764b94ceaSJussi Kivilinna 50864b94ceaSJussi Kivilinna static void __exit fini(void) 50964b94ceaSJussi Kivilinna { 51064b94ceaSJussi Kivilinna crypto_unregister_alg(&blk_ctr_alg); 51164b94ceaSJussi Kivilinna crypto_unregister_alg(&blk_cbc_alg); 51264b94ceaSJussi Kivilinna crypto_unregister_alg(&blk_ecb_alg); 51364b94ceaSJussi Kivilinna crypto_unregister_alg(&bf_alg); 51464b94ceaSJussi Kivilinna } 51564b94ceaSJussi Kivilinna 51664b94ceaSJussi Kivilinna module_init(init); 51764b94ceaSJussi Kivilinna module_exit(fini); 51864b94ceaSJussi Kivilinna 51964b94ceaSJussi Kivilinna MODULE_LICENSE("GPL"); 52064b94ceaSJussi Kivilinna MODULE_DESCRIPTION("Blowfish Cipher Algorithm, asm optimized"); 52164b94ceaSJussi Kivilinna MODULE_ALIAS("blowfish"); 52264b94ceaSJussi Kivilinna MODULE_ALIAS("blowfish-asm"); 523