18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * CBC: Cipher Block Chaining mode 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2006-2016 Herbert Xu <herbert@gondor.apana.org.au> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <crypto/algapi.h> 98c2ecf20Sopenharmony_ci#include <crypto/internal/skcipher.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/log2.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic int crypto_cbc_encrypt_segment(struct skcipher_walk *walk, 178c2ecf20Sopenharmony_ci struct crypto_skcipher *skcipher) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci unsigned int bsize = crypto_skcipher_blocksize(skcipher); 208c2ecf20Sopenharmony_ci void (*fn)(struct crypto_tfm *, u8 *, const u8 *); 218c2ecf20Sopenharmony_ci unsigned int nbytes = walk->nbytes; 228c2ecf20Sopenharmony_ci u8 *src = walk->src.virt.addr; 238c2ecf20Sopenharmony_ci u8 *dst = walk->dst.virt.addr; 248c2ecf20Sopenharmony_ci struct crypto_cipher *cipher; 258c2ecf20Sopenharmony_ci struct crypto_tfm *tfm; 268c2ecf20Sopenharmony_ci u8 *iv = walk->iv; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci cipher = skcipher_cipher_simple(skcipher); 298c2ecf20Sopenharmony_ci tfm = crypto_cipher_tfm(cipher); 308c2ecf20Sopenharmony_ci fn = crypto_cipher_alg(cipher)->cia_encrypt; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci do { 338c2ecf20Sopenharmony_ci crypto_xor(iv, src, bsize); 348c2ecf20Sopenharmony_ci fn(tfm, dst, iv); 358c2ecf20Sopenharmony_ci memcpy(iv, dst, bsize); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci src += bsize; 388c2ecf20Sopenharmony_ci dst += bsize; 398c2ecf20Sopenharmony_ci } while ((nbytes -= bsize) >= bsize); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return nbytes; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int crypto_cbc_encrypt_inplace(struct skcipher_walk *walk, 458c2ecf20Sopenharmony_ci struct crypto_skcipher *skcipher) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci unsigned int bsize = crypto_skcipher_blocksize(skcipher); 488c2ecf20Sopenharmony_ci void (*fn)(struct crypto_tfm *, u8 *, const u8 *); 498c2ecf20Sopenharmony_ci unsigned int nbytes = walk->nbytes; 508c2ecf20Sopenharmony_ci u8 *src = walk->src.virt.addr; 518c2ecf20Sopenharmony_ci struct crypto_cipher *cipher; 528c2ecf20Sopenharmony_ci struct crypto_tfm *tfm; 538c2ecf20Sopenharmony_ci u8 *iv = walk->iv; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci cipher = skcipher_cipher_simple(skcipher); 568c2ecf20Sopenharmony_ci tfm = crypto_cipher_tfm(cipher); 578c2ecf20Sopenharmony_ci fn = crypto_cipher_alg(cipher)->cia_encrypt; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci do { 608c2ecf20Sopenharmony_ci crypto_xor(src, iv, bsize); 618c2ecf20Sopenharmony_ci fn(tfm, src, src); 628c2ecf20Sopenharmony_ci iv = src; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci src += bsize; 658c2ecf20Sopenharmony_ci } while ((nbytes -= bsize) >= bsize); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci memcpy(walk->iv, iv, bsize); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return nbytes; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int crypto_cbc_encrypt(struct skcipher_request *req) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req); 758c2ecf20Sopenharmony_ci struct skcipher_walk walk; 768c2ecf20Sopenharmony_ci int err; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci err = skcipher_walk_virt(&walk, req, false); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci while (walk.nbytes) { 818c2ecf20Sopenharmony_ci if (walk.src.virt.addr == walk.dst.virt.addr) 828c2ecf20Sopenharmony_ci err = crypto_cbc_encrypt_inplace(&walk, skcipher); 838c2ecf20Sopenharmony_ci else 848c2ecf20Sopenharmony_ci err = crypto_cbc_encrypt_segment(&walk, skcipher); 858c2ecf20Sopenharmony_ci err = skcipher_walk_done(&walk, err); 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return err; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int crypto_cbc_decrypt_segment(struct skcipher_walk *walk, 928c2ecf20Sopenharmony_ci struct crypto_skcipher *skcipher) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci unsigned int bsize = crypto_skcipher_blocksize(skcipher); 958c2ecf20Sopenharmony_ci void (*fn)(struct crypto_tfm *, u8 *, const u8 *); 968c2ecf20Sopenharmony_ci unsigned int nbytes = walk->nbytes; 978c2ecf20Sopenharmony_ci u8 *src = walk->src.virt.addr; 988c2ecf20Sopenharmony_ci u8 *dst = walk->dst.virt.addr; 998c2ecf20Sopenharmony_ci struct crypto_cipher *cipher; 1008c2ecf20Sopenharmony_ci struct crypto_tfm *tfm; 1018c2ecf20Sopenharmony_ci u8 *iv = walk->iv; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci cipher = skcipher_cipher_simple(skcipher); 1048c2ecf20Sopenharmony_ci tfm = crypto_cipher_tfm(cipher); 1058c2ecf20Sopenharmony_ci fn = crypto_cipher_alg(cipher)->cia_decrypt; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci do { 1088c2ecf20Sopenharmony_ci fn(tfm, dst, src); 1098c2ecf20Sopenharmony_ci crypto_xor(dst, iv, bsize); 1108c2ecf20Sopenharmony_ci iv = src; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci src += bsize; 1138c2ecf20Sopenharmony_ci dst += bsize; 1148c2ecf20Sopenharmony_ci } while ((nbytes -= bsize) >= bsize); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci memcpy(walk->iv, iv, bsize); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return nbytes; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int crypto_cbc_decrypt_inplace(struct skcipher_walk *walk, 1228c2ecf20Sopenharmony_ci struct crypto_skcipher *skcipher) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci unsigned int bsize = crypto_skcipher_blocksize(skcipher); 1258c2ecf20Sopenharmony_ci void (*fn)(struct crypto_tfm *, u8 *, const u8 *); 1268c2ecf20Sopenharmony_ci unsigned int nbytes = walk->nbytes; 1278c2ecf20Sopenharmony_ci u8 *src = walk->src.virt.addr; 1288c2ecf20Sopenharmony_ci u8 last_iv[MAX_CIPHER_BLOCKSIZE]; 1298c2ecf20Sopenharmony_ci struct crypto_cipher *cipher; 1308c2ecf20Sopenharmony_ci struct crypto_tfm *tfm; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci cipher = skcipher_cipher_simple(skcipher); 1338c2ecf20Sopenharmony_ci tfm = crypto_cipher_tfm(cipher); 1348c2ecf20Sopenharmony_ci fn = crypto_cipher_alg(cipher)->cia_decrypt; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* Start of the last block. */ 1378c2ecf20Sopenharmony_ci src += nbytes - (nbytes & (bsize - 1)) - bsize; 1388c2ecf20Sopenharmony_ci memcpy(last_iv, src, bsize); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci for (;;) { 1418c2ecf20Sopenharmony_ci fn(tfm, src, src); 1428c2ecf20Sopenharmony_ci if ((nbytes -= bsize) < bsize) 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci crypto_xor(src, src - bsize, bsize); 1458c2ecf20Sopenharmony_ci src -= bsize; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci crypto_xor(src, walk->iv, bsize); 1498c2ecf20Sopenharmony_ci memcpy(walk->iv, last_iv, bsize); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return nbytes; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int crypto_cbc_decrypt(struct skcipher_request *req) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req); 1578c2ecf20Sopenharmony_ci struct skcipher_walk walk; 1588c2ecf20Sopenharmony_ci int err; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci err = skcipher_walk_virt(&walk, req, false); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci while (walk.nbytes) { 1638c2ecf20Sopenharmony_ci if (walk.src.virt.addr == walk.dst.virt.addr) 1648c2ecf20Sopenharmony_ci err = crypto_cbc_decrypt_inplace(&walk, skcipher); 1658c2ecf20Sopenharmony_ci else 1668c2ecf20Sopenharmony_ci err = crypto_cbc_decrypt_segment(&walk, skcipher); 1678c2ecf20Sopenharmony_ci err = skcipher_walk_done(&walk, err); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return err; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int crypto_cbc_create(struct crypto_template *tmpl, struct rtattr **tb) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct skcipher_instance *inst; 1768c2ecf20Sopenharmony_ci struct crypto_alg *alg; 1778c2ecf20Sopenharmony_ci int err; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci inst = skcipher_alloc_instance_simple(tmpl, tb); 1808c2ecf20Sopenharmony_ci if (IS_ERR(inst)) 1818c2ecf20Sopenharmony_ci return PTR_ERR(inst); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci alg = skcipher_ialg_simple(inst); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci err = -EINVAL; 1868c2ecf20Sopenharmony_ci if (!is_power_of_2(alg->cra_blocksize)) 1878c2ecf20Sopenharmony_ci goto out_free_inst; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci inst->alg.encrypt = crypto_cbc_encrypt; 1908c2ecf20Sopenharmony_ci inst->alg.decrypt = crypto_cbc_decrypt; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci err = skcipher_register_instance(tmpl, inst); 1938c2ecf20Sopenharmony_ci if (err) { 1948c2ecf20Sopenharmony_ciout_free_inst: 1958c2ecf20Sopenharmony_ci inst->free(inst); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return err; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic struct crypto_template crypto_cbc_tmpl = { 2028c2ecf20Sopenharmony_ci .name = "cbc", 2038c2ecf20Sopenharmony_ci .create = crypto_cbc_create, 2048c2ecf20Sopenharmony_ci .module = THIS_MODULE, 2058c2ecf20Sopenharmony_ci}; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int __init crypto_cbc_module_init(void) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci return crypto_register_template(&crypto_cbc_tmpl); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void __exit crypto_cbc_module_exit(void) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci crypto_unregister_template(&crypto_cbc_tmpl); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cisubsys_initcall(crypto_cbc_module_init); 2188c2ecf20Sopenharmony_cimodule_exit(crypto_cbc_module_exit); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("CBC block cipher mode of operation"); 2228c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("cbc"); 223