162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * CBC: Cipher Block Chaining mode 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2006-2016 Herbert Xu <herbert@gondor.apana.org.au> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <crypto/algapi.h> 962306a36Sopenharmony_ci#include <crypto/internal/cipher.h> 1062306a36Sopenharmony_ci#include <crypto/internal/skcipher.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/log2.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic int crypto_cbc_encrypt_segment(struct skcipher_walk *walk, 1862306a36Sopenharmony_ci struct crypto_skcipher *skcipher) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci unsigned int bsize = crypto_skcipher_blocksize(skcipher); 2162306a36Sopenharmony_ci void (*fn)(struct crypto_tfm *, u8 *, const u8 *); 2262306a36Sopenharmony_ci unsigned int nbytes = walk->nbytes; 2362306a36Sopenharmony_ci u8 *src = walk->src.virt.addr; 2462306a36Sopenharmony_ci u8 *dst = walk->dst.virt.addr; 2562306a36Sopenharmony_ci struct crypto_cipher *cipher; 2662306a36Sopenharmony_ci struct crypto_tfm *tfm; 2762306a36Sopenharmony_ci u8 *iv = walk->iv; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci cipher = skcipher_cipher_simple(skcipher); 3062306a36Sopenharmony_ci tfm = crypto_cipher_tfm(cipher); 3162306a36Sopenharmony_ci fn = crypto_cipher_alg(cipher)->cia_encrypt; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci do { 3462306a36Sopenharmony_ci crypto_xor(iv, src, bsize); 3562306a36Sopenharmony_ci fn(tfm, dst, iv); 3662306a36Sopenharmony_ci memcpy(iv, dst, bsize); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci src += bsize; 3962306a36Sopenharmony_ci dst += bsize; 4062306a36Sopenharmony_ci } while ((nbytes -= bsize) >= bsize); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return nbytes; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int crypto_cbc_encrypt_inplace(struct skcipher_walk *walk, 4662306a36Sopenharmony_ci struct crypto_skcipher *skcipher) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci unsigned int bsize = crypto_skcipher_blocksize(skcipher); 4962306a36Sopenharmony_ci void (*fn)(struct crypto_tfm *, u8 *, const u8 *); 5062306a36Sopenharmony_ci unsigned int nbytes = walk->nbytes; 5162306a36Sopenharmony_ci u8 *src = walk->src.virt.addr; 5262306a36Sopenharmony_ci struct crypto_cipher *cipher; 5362306a36Sopenharmony_ci struct crypto_tfm *tfm; 5462306a36Sopenharmony_ci u8 *iv = walk->iv; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci cipher = skcipher_cipher_simple(skcipher); 5762306a36Sopenharmony_ci tfm = crypto_cipher_tfm(cipher); 5862306a36Sopenharmony_ci fn = crypto_cipher_alg(cipher)->cia_encrypt; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci do { 6162306a36Sopenharmony_ci crypto_xor(src, iv, bsize); 6262306a36Sopenharmony_ci fn(tfm, src, src); 6362306a36Sopenharmony_ci iv = src; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci src += bsize; 6662306a36Sopenharmony_ci } while ((nbytes -= bsize) >= bsize); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci memcpy(walk->iv, iv, bsize); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return nbytes; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int crypto_cbc_encrypt(struct skcipher_request *req) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req); 7662306a36Sopenharmony_ci struct skcipher_walk walk; 7762306a36Sopenharmony_ci int err; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci err = skcipher_walk_virt(&walk, req, false); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci while (walk.nbytes) { 8262306a36Sopenharmony_ci if (walk.src.virt.addr == walk.dst.virt.addr) 8362306a36Sopenharmony_ci err = crypto_cbc_encrypt_inplace(&walk, skcipher); 8462306a36Sopenharmony_ci else 8562306a36Sopenharmony_ci err = crypto_cbc_encrypt_segment(&walk, skcipher); 8662306a36Sopenharmony_ci err = skcipher_walk_done(&walk, err); 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return err; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int crypto_cbc_decrypt_segment(struct skcipher_walk *walk, 9362306a36Sopenharmony_ci struct crypto_skcipher *skcipher) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci unsigned int bsize = crypto_skcipher_blocksize(skcipher); 9662306a36Sopenharmony_ci void (*fn)(struct crypto_tfm *, u8 *, const u8 *); 9762306a36Sopenharmony_ci unsigned int nbytes = walk->nbytes; 9862306a36Sopenharmony_ci u8 *src = walk->src.virt.addr; 9962306a36Sopenharmony_ci u8 *dst = walk->dst.virt.addr; 10062306a36Sopenharmony_ci struct crypto_cipher *cipher; 10162306a36Sopenharmony_ci struct crypto_tfm *tfm; 10262306a36Sopenharmony_ci u8 *iv = walk->iv; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci cipher = skcipher_cipher_simple(skcipher); 10562306a36Sopenharmony_ci tfm = crypto_cipher_tfm(cipher); 10662306a36Sopenharmony_ci fn = crypto_cipher_alg(cipher)->cia_decrypt; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci do { 10962306a36Sopenharmony_ci fn(tfm, dst, src); 11062306a36Sopenharmony_ci crypto_xor(dst, iv, bsize); 11162306a36Sopenharmony_ci iv = src; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci src += bsize; 11462306a36Sopenharmony_ci dst += bsize; 11562306a36Sopenharmony_ci } while ((nbytes -= bsize) >= bsize); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci memcpy(walk->iv, iv, bsize); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return nbytes; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int crypto_cbc_decrypt_inplace(struct skcipher_walk *walk, 12362306a36Sopenharmony_ci struct crypto_skcipher *skcipher) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci unsigned int bsize = crypto_skcipher_blocksize(skcipher); 12662306a36Sopenharmony_ci void (*fn)(struct crypto_tfm *, u8 *, const u8 *); 12762306a36Sopenharmony_ci unsigned int nbytes = walk->nbytes; 12862306a36Sopenharmony_ci u8 *src = walk->src.virt.addr; 12962306a36Sopenharmony_ci u8 last_iv[MAX_CIPHER_BLOCKSIZE]; 13062306a36Sopenharmony_ci struct crypto_cipher *cipher; 13162306a36Sopenharmony_ci struct crypto_tfm *tfm; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci cipher = skcipher_cipher_simple(skcipher); 13462306a36Sopenharmony_ci tfm = crypto_cipher_tfm(cipher); 13562306a36Sopenharmony_ci fn = crypto_cipher_alg(cipher)->cia_decrypt; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* Start of the last block. */ 13862306a36Sopenharmony_ci src += nbytes - (nbytes & (bsize - 1)) - bsize; 13962306a36Sopenharmony_ci memcpy(last_iv, src, bsize); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci for (;;) { 14262306a36Sopenharmony_ci fn(tfm, src, src); 14362306a36Sopenharmony_ci if ((nbytes -= bsize) < bsize) 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci crypto_xor(src, src - bsize, bsize); 14662306a36Sopenharmony_ci src -= bsize; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci crypto_xor(src, walk->iv, bsize); 15062306a36Sopenharmony_ci memcpy(walk->iv, last_iv, bsize); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return nbytes; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int crypto_cbc_decrypt(struct skcipher_request *req) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req); 15862306a36Sopenharmony_ci struct skcipher_walk walk; 15962306a36Sopenharmony_ci int err; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci err = skcipher_walk_virt(&walk, req, false); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci while (walk.nbytes) { 16462306a36Sopenharmony_ci if (walk.src.virt.addr == walk.dst.virt.addr) 16562306a36Sopenharmony_ci err = crypto_cbc_decrypt_inplace(&walk, skcipher); 16662306a36Sopenharmony_ci else 16762306a36Sopenharmony_ci err = crypto_cbc_decrypt_segment(&walk, skcipher); 16862306a36Sopenharmony_ci err = skcipher_walk_done(&walk, err); 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return err; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int crypto_cbc_create(struct crypto_template *tmpl, struct rtattr **tb) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct skcipher_instance *inst; 17762306a36Sopenharmony_ci struct crypto_alg *alg; 17862306a36Sopenharmony_ci int err; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci inst = skcipher_alloc_instance_simple(tmpl, tb); 18162306a36Sopenharmony_ci if (IS_ERR(inst)) 18262306a36Sopenharmony_ci return PTR_ERR(inst); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci alg = skcipher_ialg_simple(inst); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci err = -EINVAL; 18762306a36Sopenharmony_ci if (!is_power_of_2(alg->cra_blocksize)) 18862306a36Sopenharmony_ci goto out_free_inst; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci inst->alg.encrypt = crypto_cbc_encrypt; 19162306a36Sopenharmony_ci inst->alg.decrypt = crypto_cbc_decrypt; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci err = skcipher_register_instance(tmpl, inst); 19462306a36Sopenharmony_ci if (err) { 19562306a36Sopenharmony_ciout_free_inst: 19662306a36Sopenharmony_ci inst->free(inst); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return err; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic struct crypto_template crypto_cbc_tmpl = { 20362306a36Sopenharmony_ci .name = "cbc", 20462306a36Sopenharmony_ci .create = crypto_cbc_create, 20562306a36Sopenharmony_ci .module = THIS_MODULE, 20662306a36Sopenharmony_ci}; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int __init crypto_cbc_module_init(void) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci return crypto_register_template(&crypto_cbc_tmpl); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic void __exit crypto_cbc_module_exit(void) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci crypto_unregister_template(&crypto_cbc_tmpl); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cisubsys_initcall(crypto_cbc_module_init); 21962306a36Sopenharmony_cimodule_exit(crypto_cbc_module_exit); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 22262306a36Sopenharmony_ciMODULE_DESCRIPTION("CBC block cipher mode of operation"); 22362306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("cbc"); 224