11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 264b94ceaSJussi Kivilinna /* 364b94ceaSJussi Kivilinna * Glue Code for assembler optimized version of Blowfish 464b94ceaSJussi Kivilinna * 53d387ef0SJussi Kivilinna * Copyright (c) 2011 Jussi Kivilinna <jussi.kivilinna@mbnet.fi> 664b94ceaSJussi Kivilinna * 7a071d06eSJussi Kivilinna * CBC & ECB parts based on code (crypto/cbc.c,ecb.c) by: 8a071d06eSJussi Kivilinna * Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au> 964b94ceaSJussi Kivilinna */ 1064b94ceaSJussi Kivilinna 11c1679171SEric Biggers #include <crypto/algapi.h> 1264b94ceaSJussi Kivilinna #include <crypto/blowfish.h> 13c1679171SEric Biggers #include <crypto/internal/skcipher.h> 1464b94ceaSJussi Kivilinna #include <linux/crypto.h> 1564b94ceaSJussi Kivilinna #include <linux/init.h> 1664b94ceaSJussi Kivilinna #include <linux/module.h> 1764b94ceaSJussi Kivilinna #include <linux/types.h> 1864b94ceaSJussi Kivilinna 1964b94ceaSJussi Kivilinna /* regular block cipher functions */ 2064b94ceaSJussi Kivilinna asmlinkage void __blowfish_enc_blk(struct bf_ctx *ctx, u8 *dst, const u8 *src, 2164b94ceaSJussi Kivilinna bool xor); 2264b94ceaSJussi Kivilinna asmlinkage void blowfish_dec_blk(struct bf_ctx *ctx, u8 *dst, const u8 *src); 2364b94ceaSJussi Kivilinna 2464b94ceaSJussi Kivilinna /* 4-way parallel cipher functions */ 2564b94ceaSJussi Kivilinna asmlinkage void __blowfish_enc_blk_4way(struct bf_ctx *ctx, u8 *dst, 2664b94ceaSJussi Kivilinna const u8 *src, bool xor); 2764b94ceaSJussi Kivilinna asmlinkage void blowfish_dec_blk_4way(struct bf_ctx *ctx, u8 *dst, 2864b94ceaSJussi Kivilinna const u8 *src); 293d387ef0SJussi Kivilinna 303d387ef0SJussi Kivilinna static inline void blowfish_enc_blk(struct bf_ctx *ctx, u8 *dst, const u8 *src) 313d387ef0SJussi Kivilinna { 323d387ef0SJussi Kivilinna __blowfish_enc_blk(ctx, dst, src, false); 333d387ef0SJussi Kivilinna } 343d387ef0SJussi Kivilinna 353d387ef0SJussi Kivilinna static inline void blowfish_enc_blk_4way(struct bf_ctx *ctx, u8 *dst, 363d387ef0SJussi Kivilinna const u8 *src) 373d387ef0SJussi Kivilinna { 383d387ef0SJussi Kivilinna __blowfish_enc_blk_4way(ctx, dst, src, false); 393d387ef0SJussi Kivilinna } 403d387ef0SJussi Kivilinna 4164b94ceaSJussi Kivilinna static void blowfish_encrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) 4264b94ceaSJussi Kivilinna { 4364b94ceaSJussi Kivilinna blowfish_enc_blk(crypto_tfm_ctx(tfm), dst, src); 4464b94ceaSJussi Kivilinna } 4564b94ceaSJussi Kivilinna 4664b94ceaSJussi Kivilinna static void blowfish_decrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) 4764b94ceaSJussi Kivilinna { 4864b94ceaSJussi Kivilinna blowfish_dec_blk(crypto_tfm_ctx(tfm), dst, src); 4964b94ceaSJussi Kivilinna } 5064b94ceaSJussi Kivilinna 51c1679171SEric Biggers static int blowfish_setkey_skcipher(struct crypto_skcipher *tfm, 52c1679171SEric Biggers const u8 *key, unsigned int keylen) 53c1679171SEric Biggers { 54c1679171SEric Biggers return blowfish_setkey(&tfm->base, key, keylen); 55c1679171SEric Biggers } 56c1679171SEric Biggers 57c1679171SEric Biggers static int ecb_crypt(struct skcipher_request *req, 5864b94ceaSJussi Kivilinna void (*fn)(struct bf_ctx *, u8 *, const u8 *), 5964b94ceaSJussi Kivilinna void (*fn_4way)(struct bf_ctx *, u8 *, const u8 *)) 6064b94ceaSJussi Kivilinna { 6164b94ceaSJussi Kivilinna unsigned int bsize = BF_BLOCK_SIZE; 62c1679171SEric Biggers struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 63c1679171SEric Biggers struct bf_ctx *ctx = crypto_skcipher_ctx(tfm); 64c1679171SEric Biggers struct skcipher_walk walk; 6564b94ceaSJussi Kivilinna unsigned int nbytes; 6664b94ceaSJussi Kivilinna int err; 6764b94ceaSJussi Kivilinna 68c1679171SEric Biggers err = skcipher_walk_virt(&walk, req, false); 6964b94ceaSJussi Kivilinna 70c1679171SEric Biggers while ((nbytes = walk.nbytes)) { 71c1679171SEric Biggers u8 *wsrc = walk.src.virt.addr; 72c1679171SEric Biggers u8 *wdst = walk.dst.virt.addr; 7364b94ceaSJussi Kivilinna 7464b94ceaSJussi Kivilinna /* Process four block batch */ 7564b94ceaSJussi Kivilinna if (nbytes >= bsize * 4) { 7664b94ceaSJussi Kivilinna do { 7764b94ceaSJussi Kivilinna fn_4way(ctx, wdst, wsrc); 7864b94ceaSJussi Kivilinna 7964b94ceaSJussi Kivilinna wsrc += bsize * 4; 8064b94ceaSJussi Kivilinna wdst += bsize * 4; 8164b94ceaSJussi Kivilinna nbytes -= bsize * 4; 8264b94ceaSJussi Kivilinna } while (nbytes >= bsize * 4); 8364b94ceaSJussi Kivilinna 8464b94ceaSJussi Kivilinna if (nbytes < bsize) 8564b94ceaSJussi Kivilinna goto done; 8664b94ceaSJussi Kivilinna } 8764b94ceaSJussi Kivilinna 8864b94ceaSJussi Kivilinna /* Handle leftovers */ 8964b94ceaSJussi Kivilinna do { 9064b94ceaSJussi Kivilinna fn(ctx, wdst, wsrc); 9164b94ceaSJussi Kivilinna 9264b94ceaSJussi Kivilinna wsrc += bsize; 9364b94ceaSJussi Kivilinna wdst += bsize; 9464b94ceaSJussi Kivilinna nbytes -= bsize; 9564b94ceaSJussi Kivilinna } while (nbytes >= bsize); 9664b94ceaSJussi Kivilinna 9764b94ceaSJussi Kivilinna done: 98c1679171SEric Biggers err = skcipher_walk_done(&walk, nbytes); 9964b94ceaSJussi Kivilinna } 10064b94ceaSJussi Kivilinna 10164b94ceaSJussi Kivilinna return err; 10264b94ceaSJussi Kivilinna } 10364b94ceaSJussi Kivilinna 104c1679171SEric Biggers static int ecb_encrypt(struct skcipher_request *req) 10564b94ceaSJussi Kivilinna { 106c1679171SEric Biggers return ecb_crypt(req, blowfish_enc_blk, blowfish_enc_blk_4way); 10764b94ceaSJussi Kivilinna } 10864b94ceaSJussi Kivilinna 109c1679171SEric Biggers static int ecb_decrypt(struct skcipher_request *req) 11064b94ceaSJussi Kivilinna { 111c1679171SEric Biggers return ecb_crypt(req, blowfish_dec_blk, blowfish_dec_blk_4way); 11264b94ceaSJussi Kivilinna } 11364b94ceaSJussi Kivilinna 114c1679171SEric Biggers static unsigned int __cbc_encrypt(struct bf_ctx *ctx, 115c1679171SEric Biggers struct skcipher_walk *walk) 11664b94ceaSJussi Kivilinna { 11764b94ceaSJussi Kivilinna unsigned int bsize = BF_BLOCK_SIZE; 11864b94ceaSJussi Kivilinna unsigned int nbytes = walk->nbytes; 11964b94ceaSJussi Kivilinna u64 *src = (u64 *)walk->src.virt.addr; 12064b94ceaSJussi Kivilinna u64 *dst = (u64 *)walk->dst.virt.addr; 12164b94ceaSJussi Kivilinna u64 *iv = (u64 *)walk->iv; 12264b94ceaSJussi Kivilinna 12364b94ceaSJussi Kivilinna do { 12464b94ceaSJussi Kivilinna *dst = *src ^ *iv; 12564b94ceaSJussi Kivilinna blowfish_enc_blk(ctx, (u8 *)dst, (u8 *)dst); 12664b94ceaSJussi Kivilinna iv = dst; 12764b94ceaSJussi Kivilinna 12864b94ceaSJussi Kivilinna src += 1; 12964b94ceaSJussi Kivilinna dst += 1; 13064b94ceaSJussi Kivilinna nbytes -= bsize; 13164b94ceaSJussi Kivilinna } while (nbytes >= bsize); 13264b94ceaSJussi Kivilinna 13364b94ceaSJussi Kivilinna *(u64 *)walk->iv = *iv; 13464b94ceaSJussi Kivilinna return nbytes; 13564b94ceaSJussi Kivilinna } 13664b94ceaSJussi Kivilinna 137c1679171SEric Biggers static int cbc_encrypt(struct skcipher_request *req) 13864b94ceaSJussi Kivilinna { 139c1679171SEric Biggers struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 140c1679171SEric Biggers struct bf_ctx *ctx = crypto_skcipher_ctx(tfm); 141c1679171SEric Biggers struct skcipher_walk walk; 142c1679171SEric Biggers unsigned int nbytes; 14364b94ceaSJussi Kivilinna int err; 14464b94ceaSJussi Kivilinna 145c1679171SEric Biggers err = skcipher_walk_virt(&walk, req, false); 14664b94ceaSJussi Kivilinna 14764b94ceaSJussi Kivilinna while ((nbytes = walk.nbytes)) { 148c1679171SEric Biggers nbytes = __cbc_encrypt(ctx, &walk); 149c1679171SEric Biggers err = skcipher_walk_done(&walk, nbytes); 15064b94ceaSJussi Kivilinna } 15164b94ceaSJussi Kivilinna 15264b94ceaSJussi Kivilinna return err; 15364b94ceaSJussi Kivilinna } 15464b94ceaSJussi Kivilinna 155c1679171SEric Biggers static unsigned int __cbc_decrypt(struct bf_ctx *ctx, 156c1679171SEric Biggers struct skcipher_walk *walk) 15764b94ceaSJussi Kivilinna { 15864b94ceaSJussi Kivilinna unsigned int bsize = BF_BLOCK_SIZE; 15964b94ceaSJussi Kivilinna unsigned int nbytes = walk->nbytes; 16064b94ceaSJussi Kivilinna u64 *src = (u64 *)walk->src.virt.addr; 16164b94ceaSJussi Kivilinna u64 *dst = (u64 *)walk->dst.virt.addr; 16264b94ceaSJussi Kivilinna u64 ivs[4 - 1]; 16364b94ceaSJussi Kivilinna u64 last_iv; 16464b94ceaSJussi Kivilinna 16564b94ceaSJussi Kivilinna /* Start of the last block. */ 16664b94ceaSJussi Kivilinna src += nbytes / bsize - 1; 16764b94ceaSJussi Kivilinna dst += nbytes / bsize - 1; 16864b94ceaSJussi Kivilinna 16964b94ceaSJussi Kivilinna last_iv = *src; 17064b94ceaSJussi Kivilinna 17164b94ceaSJussi Kivilinna /* Process four block batch */ 17264b94ceaSJussi Kivilinna if (nbytes >= bsize * 4) { 17364b94ceaSJussi Kivilinna do { 17464b94ceaSJussi Kivilinna nbytes -= bsize * 4 - bsize; 17564b94ceaSJussi Kivilinna src -= 4 - 1; 17664b94ceaSJussi Kivilinna dst -= 4 - 1; 17764b94ceaSJussi Kivilinna 17864b94ceaSJussi Kivilinna ivs[0] = src[0]; 17964b94ceaSJussi Kivilinna ivs[1] = src[1]; 18064b94ceaSJussi Kivilinna ivs[2] = src[2]; 18164b94ceaSJussi Kivilinna 18264b94ceaSJussi Kivilinna blowfish_dec_blk_4way(ctx, (u8 *)dst, (u8 *)src); 18364b94ceaSJussi Kivilinna 18464b94ceaSJussi Kivilinna dst[1] ^= ivs[0]; 18564b94ceaSJussi Kivilinna dst[2] ^= ivs[1]; 18664b94ceaSJussi Kivilinna dst[3] ^= ivs[2]; 18764b94ceaSJussi Kivilinna 18864b94ceaSJussi Kivilinna nbytes -= bsize; 18964b94ceaSJussi Kivilinna if (nbytes < bsize) 19064b94ceaSJussi Kivilinna goto done; 19164b94ceaSJussi Kivilinna 19264b94ceaSJussi Kivilinna *dst ^= *(src - 1); 19364b94ceaSJussi Kivilinna src -= 1; 19464b94ceaSJussi Kivilinna dst -= 1; 19564b94ceaSJussi Kivilinna } while (nbytes >= bsize * 4); 19664b94ceaSJussi Kivilinna } 19764b94ceaSJussi Kivilinna 19864b94ceaSJussi Kivilinna /* Handle leftovers */ 19964b94ceaSJussi Kivilinna for (;;) { 20064b94ceaSJussi Kivilinna blowfish_dec_blk(ctx, (u8 *)dst, (u8 *)src); 20164b94ceaSJussi Kivilinna 20264b94ceaSJussi Kivilinna nbytes -= bsize; 20364b94ceaSJussi Kivilinna if (nbytes < bsize) 20464b94ceaSJussi Kivilinna break; 20564b94ceaSJussi Kivilinna 20664b94ceaSJussi Kivilinna *dst ^= *(src - 1); 20764b94ceaSJussi Kivilinna src -= 1; 20864b94ceaSJussi Kivilinna dst -= 1; 20964b94ceaSJussi Kivilinna } 21064b94ceaSJussi Kivilinna 21164b94ceaSJussi Kivilinna done: 21264b94ceaSJussi Kivilinna *dst ^= *(u64 *)walk->iv; 21364b94ceaSJussi Kivilinna *(u64 *)walk->iv = last_iv; 21464b94ceaSJussi Kivilinna 21564b94ceaSJussi Kivilinna return nbytes; 21664b94ceaSJussi Kivilinna } 21764b94ceaSJussi Kivilinna 218c1679171SEric Biggers static int cbc_decrypt(struct skcipher_request *req) 21964b94ceaSJussi Kivilinna { 220c1679171SEric Biggers struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 221c1679171SEric Biggers struct bf_ctx *ctx = crypto_skcipher_ctx(tfm); 222c1679171SEric Biggers struct skcipher_walk walk; 223c1679171SEric Biggers unsigned int nbytes; 22464b94ceaSJussi Kivilinna int err; 22564b94ceaSJussi Kivilinna 226c1679171SEric Biggers err = skcipher_walk_virt(&walk, req, false); 22764b94ceaSJussi Kivilinna 22864b94ceaSJussi Kivilinna while ((nbytes = walk.nbytes)) { 229c1679171SEric Biggers nbytes = __cbc_decrypt(ctx, &walk); 230c1679171SEric Biggers err = skcipher_walk_done(&walk, nbytes); 23164b94ceaSJussi Kivilinna } 23264b94ceaSJussi Kivilinna 23364b94ceaSJussi Kivilinna return err; 23464b94ceaSJussi Kivilinna } 23564b94ceaSJussi Kivilinna 236c1679171SEric Biggers static struct crypto_alg bf_cipher_alg = { 237d433208cSJussi Kivilinna .cra_name = "blowfish", 238d433208cSJussi Kivilinna .cra_driver_name = "blowfish-asm", 239d433208cSJussi Kivilinna .cra_priority = 200, 240d433208cSJussi Kivilinna .cra_flags = CRYPTO_ALG_TYPE_CIPHER, 241d433208cSJussi Kivilinna .cra_blocksize = BF_BLOCK_SIZE, 242d433208cSJussi Kivilinna .cra_ctxsize = sizeof(struct bf_ctx), 243919e2c32SJussi Kivilinna .cra_alignmask = 0, 244d433208cSJussi Kivilinna .cra_module = THIS_MODULE, 245d433208cSJussi Kivilinna .cra_u = { 246d433208cSJussi Kivilinna .cipher = { 247d433208cSJussi Kivilinna .cia_min_keysize = BF_MIN_KEY_SIZE, 248d433208cSJussi Kivilinna .cia_max_keysize = BF_MAX_KEY_SIZE, 249d433208cSJussi Kivilinna .cia_setkey = blowfish_setkey, 250d433208cSJussi Kivilinna .cia_encrypt = blowfish_encrypt, 251d433208cSJussi Kivilinna .cia_decrypt = blowfish_decrypt, 252d433208cSJussi Kivilinna } 253d433208cSJussi Kivilinna } 254c1679171SEric Biggers }; 255c1679171SEric Biggers 256c1679171SEric Biggers static struct skcipher_alg bf_skcipher_algs[] = { 257c1679171SEric Biggers { 258c1679171SEric Biggers .base.cra_name = "ecb(blowfish)", 259c1679171SEric Biggers .base.cra_driver_name = "ecb-blowfish-asm", 260c1679171SEric Biggers .base.cra_priority = 300, 261c1679171SEric Biggers .base.cra_blocksize = BF_BLOCK_SIZE, 262c1679171SEric Biggers .base.cra_ctxsize = sizeof(struct bf_ctx), 263c1679171SEric Biggers .base.cra_module = THIS_MODULE, 264d433208cSJussi Kivilinna .min_keysize = BF_MIN_KEY_SIZE, 265d433208cSJussi Kivilinna .max_keysize = BF_MAX_KEY_SIZE, 266c1679171SEric Biggers .setkey = blowfish_setkey_skcipher, 267d433208cSJussi Kivilinna .encrypt = ecb_encrypt, 268d433208cSJussi Kivilinna .decrypt = ecb_decrypt, 269d433208cSJussi Kivilinna }, { 270c1679171SEric Biggers .base.cra_name = "cbc(blowfish)", 271c1679171SEric Biggers .base.cra_driver_name = "cbc-blowfish-asm", 272c1679171SEric Biggers .base.cra_priority = 300, 273c1679171SEric Biggers .base.cra_blocksize = BF_BLOCK_SIZE, 274c1679171SEric Biggers .base.cra_ctxsize = sizeof(struct bf_ctx), 275c1679171SEric Biggers .base.cra_module = THIS_MODULE, 276d433208cSJussi Kivilinna .min_keysize = BF_MIN_KEY_SIZE, 277d433208cSJussi Kivilinna .max_keysize = BF_MAX_KEY_SIZE, 278d433208cSJussi Kivilinna .ivsize = BF_BLOCK_SIZE, 279c1679171SEric Biggers .setkey = blowfish_setkey_skcipher, 280d433208cSJussi Kivilinna .encrypt = cbc_encrypt, 281d433208cSJussi Kivilinna .decrypt = cbc_decrypt, 28264b94ceaSJussi Kivilinna }, 283c1679171SEric Biggers }; 28464b94ceaSJussi Kivilinna 2854c58464bSJussi Kivilinna static bool is_blacklisted_cpu(void) 2864c58464bSJussi Kivilinna { 2874c58464bSJussi Kivilinna if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) 2884c58464bSJussi Kivilinna return false; 2894c58464bSJussi Kivilinna 2904c58464bSJussi Kivilinna if (boot_cpu_data.x86 == 0x0f) { 2914c58464bSJussi Kivilinna /* 2924c58464bSJussi Kivilinna * On Pentium 4, blowfish-x86_64 is slower than generic C 2934c58464bSJussi Kivilinna * implementation because use of 64bit rotates (which are really 2944c58464bSJussi Kivilinna * slow on P4). Therefore blacklist P4s. 2954c58464bSJussi Kivilinna */ 2964c58464bSJussi Kivilinna return true; 2974c58464bSJussi Kivilinna } 2984c58464bSJussi Kivilinna 2994c58464bSJussi Kivilinna return false; 3004c58464bSJussi Kivilinna } 3014c58464bSJussi Kivilinna 3024c58464bSJussi Kivilinna static int force; 3034c58464bSJussi Kivilinna module_param(force, int, 0); 3044c58464bSJussi Kivilinna MODULE_PARM_DESC(force, "Force module load, ignore CPU blacklist"); 3054c58464bSJussi Kivilinna 306*f16a005cSRandy Dunlap static int __init blowfish_init(void) 30764b94ceaSJussi Kivilinna { 308c1679171SEric Biggers int err; 309c1679171SEric Biggers 3104c58464bSJussi Kivilinna if (!force && is_blacklisted_cpu()) { 3114c58464bSJussi Kivilinna printk(KERN_INFO 3124c58464bSJussi Kivilinna "blowfish-x86_64: performance on this CPU " 3134c58464bSJussi Kivilinna "would be suboptimal: disabling " 3144c58464bSJussi Kivilinna "blowfish-x86_64.\n"); 3154c58464bSJussi Kivilinna return -ENODEV; 3164c58464bSJussi Kivilinna } 3174c58464bSJussi Kivilinna 318c1679171SEric Biggers err = crypto_register_alg(&bf_cipher_alg); 319c1679171SEric Biggers if (err) 320c1679171SEric Biggers return err; 321c1679171SEric Biggers 322c1679171SEric Biggers err = crypto_register_skciphers(bf_skcipher_algs, 323c1679171SEric Biggers ARRAY_SIZE(bf_skcipher_algs)); 324c1679171SEric Biggers if (err) 325c1679171SEric Biggers crypto_unregister_alg(&bf_cipher_alg); 326c1679171SEric Biggers 327c1679171SEric Biggers return err; 32864b94ceaSJussi Kivilinna } 32964b94ceaSJussi Kivilinna 330*f16a005cSRandy Dunlap static void __exit blowfish_fini(void) 33164b94ceaSJussi Kivilinna { 332c1679171SEric Biggers crypto_unregister_alg(&bf_cipher_alg); 333c1679171SEric Biggers crypto_unregister_skciphers(bf_skcipher_algs, 334c1679171SEric Biggers ARRAY_SIZE(bf_skcipher_algs)); 33564b94ceaSJussi Kivilinna } 33664b94ceaSJussi Kivilinna 337*f16a005cSRandy Dunlap module_init(blowfish_init); 338*f16a005cSRandy Dunlap module_exit(blowfish_fini); 33964b94ceaSJussi Kivilinna 34064b94ceaSJussi Kivilinna MODULE_LICENSE("GPL"); 34164b94ceaSJussi Kivilinna MODULE_DESCRIPTION("Blowfish Cipher Algorithm, asm optimized"); 3425d26a105SKees Cook MODULE_ALIAS_CRYPTO("blowfish"); 3435d26a105SKees Cook MODULE_ALIAS_CRYPTO("blowfish-asm"); 344