18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PCBC: Propagating Cipher Block Chaining mode 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. 68c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Derived from cbc.c 98c2ecf20Sopenharmony_ci * - Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <crypto/algapi.h> 138c2ecf20Sopenharmony_ci#include <crypto/internal/skcipher.h> 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic int crypto_pcbc_encrypt_segment(struct skcipher_request *req, 208c2ecf20Sopenharmony_ci struct skcipher_walk *walk, 218c2ecf20Sopenharmony_ci struct crypto_cipher *tfm) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci int bsize = crypto_cipher_blocksize(tfm); 248c2ecf20Sopenharmony_ci unsigned int nbytes = walk->nbytes; 258c2ecf20Sopenharmony_ci u8 *src = walk->src.virt.addr; 268c2ecf20Sopenharmony_ci u8 *dst = walk->dst.virt.addr; 278c2ecf20Sopenharmony_ci u8 * const iv = walk->iv; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci do { 308c2ecf20Sopenharmony_ci crypto_xor(iv, src, bsize); 318c2ecf20Sopenharmony_ci crypto_cipher_encrypt_one(tfm, dst, iv); 328c2ecf20Sopenharmony_ci crypto_xor_cpy(iv, dst, src, bsize); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci src += bsize; 358c2ecf20Sopenharmony_ci dst += bsize; 368c2ecf20Sopenharmony_ci } while ((nbytes -= bsize) >= bsize); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci return nbytes; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int crypto_pcbc_encrypt_inplace(struct skcipher_request *req, 428c2ecf20Sopenharmony_ci struct skcipher_walk *walk, 438c2ecf20Sopenharmony_ci struct crypto_cipher *tfm) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci int bsize = crypto_cipher_blocksize(tfm); 468c2ecf20Sopenharmony_ci unsigned int nbytes = walk->nbytes; 478c2ecf20Sopenharmony_ci u8 *src = walk->src.virt.addr; 488c2ecf20Sopenharmony_ci u8 * const iv = walk->iv; 498c2ecf20Sopenharmony_ci u8 tmpbuf[MAX_CIPHER_BLOCKSIZE]; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci do { 528c2ecf20Sopenharmony_ci memcpy(tmpbuf, src, bsize); 538c2ecf20Sopenharmony_ci crypto_xor(iv, src, bsize); 548c2ecf20Sopenharmony_ci crypto_cipher_encrypt_one(tfm, src, iv); 558c2ecf20Sopenharmony_ci crypto_xor_cpy(iv, tmpbuf, src, bsize); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci src += bsize; 588c2ecf20Sopenharmony_ci } while ((nbytes -= bsize) >= bsize); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return nbytes; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int crypto_pcbc_encrypt(struct skcipher_request *req) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 668c2ecf20Sopenharmony_ci struct crypto_cipher *cipher = skcipher_cipher_simple(tfm); 678c2ecf20Sopenharmony_ci struct skcipher_walk walk; 688c2ecf20Sopenharmony_ci unsigned int nbytes; 698c2ecf20Sopenharmony_ci int err; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci err = skcipher_walk_virt(&walk, req, false); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci while ((nbytes = walk.nbytes)) { 748c2ecf20Sopenharmony_ci if (walk.src.virt.addr == walk.dst.virt.addr) 758c2ecf20Sopenharmony_ci nbytes = crypto_pcbc_encrypt_inplace(req, &walk, 768c2ecf20Sopenharmony_ci cipher); 778c2ecf20Sopenharmony_ci else 788c2ecf20Sopenharmony_ci nbytes = crypto_pcbc_encrypt_segment(req, &walk, 798c2ecf20Sopenharmony_ci cipher); 808c2ecf20Sopenharmony_ci err = skcipher_walk_done(&walk, nbytes); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return err; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int crypto_pcbc_decrypt_segment(struct skcipher_request *req, 878c2ecf20Sopenharmony_ci struct skcipher_walk *walk, 888c2ecf20Sopenharmony_ci struct crypto_cipher *tfm) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci int bsize = crypto_cipher_blocksize(tfm); 918c2ecf20Sopenharmony_ci unsigned int nbytes = walk->nbytes; 928c2ecf20Sopenharmony_ci u8 *src = walk->src.virt.addr; 938c2ecf20Sopenharmony_ci u8 *dst = walk->dst.virt.addr; 948c2ecf20Sopenharmony_ci u8 * const iv = walk->iv; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci do { 978c2ecf20Sopenharmony_ci crypto_cipher_decrypt_one(tfm, dst, src); 988c2ecf20Sopenharmony_ci crypto_xor(dst, iv, bsize); 998c2ecf20Sopenharmony_ci crypto_xor_cpy(iv, dst, src, bsize); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci src += bsize; 1028c2ecf20Sopenharmony_ci dst += bsize; 1038c2ecf20Sopenharmony_ci } while ((nbytes -= bsize) >= bsize); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return nbytes; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic int crypto_pcbc_decrypt_inplace(struct skcipher_request *req, 1098c2ecf20Sopenharmony_ci struct skcipher_walk *walk, 1108c2ecf20Sopenharmony_ci struct crypto_cipher *tfm) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci int bsize = crypto_cipher_blocksize(tfm); 1138c2ecf20Sopenharmony_ci unsigned int nbytes = walk->nbytes; 1148c2ecf20Sopenharmony_ci u8 *src = walk->src.virt.addr; 1158c2ecf20Sopenharmony_ci u8 * const iv = walk->iv; 1168c2ecf20Sopenharmony_ci u8 tmpbuf[MAX_CIPHER_BLOCKSIZE] __aligned(__alignof__(u32)); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci do { 1198c2ecf20Sopenharmony_ci memcpy(tmpbuf, src, bsize); 1208c2ecf20Sopenharmony_ci crypto_cipher_decrypt_one(tfm, src, src); 1218c2ecf20Sopenharmony_ci crypto_xor(src, iv, bsize); 1228c2ecf20Sopenharmony_ci crypto_xor_cpy(iv, src, tmpbuf, bsize); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci src += bsize; 1258c2ecf20Sopenharmony_ci } while ((nbytes -= bsize) >= bsize); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return nbytes; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int crypto_pcbc_decrypt(struct skcipher_request *req) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 1338c2ecf20Sopenharmony_ci struct crypto_cipher *cipher = skcipher_cipher_simple(tfm); 1348c2ecf20Sopenharmony_ci struct skcipher_walk walk; 1358c2ecf20Sopenharmony_ci unsigned int nbytes; 1368c2ecf20Sopenharmony_ci int err; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci err = skcipher_walk_virt(&walk, req, false); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci while ((nbytes = walk.nbytes)) { 1418c2ecf20Sopenharmony_ci if (walk.src.virt.addr == walk.dst.virt.addr) 1428c2ecf20Sopenharmony_ci nbytes = crypto_pcbc_decrypt_inplace(req, &walk, 1438c2ecf20Sopenharmony_ci cipher); 1448c2ecf20Sopenharmony_ci else 1458c2ecf20Sopenharmony_ci nbytes = crypto_pcbc_decrypt_segment(req, &walk, 1468c2ecf20Sopenharmony_ci cipher); 1478c2ecf20Sopenharmony_ci err = skcipher_walk_done(&walk, nbytes); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return err; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct skcipher_instance *inst; 1568c2ecf20Sopenharmony_ci int err; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci inst = skcipher_alloc_instance_simple(tmpl, tb); 1598c2ecf20Sopenharmony_ci if (IS_ERR(inst)) 1608c2ecf20Sopenharmony_ci return PTR_ERR(inst); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci inst->alg.encrypt = crypto_pcbc_encrypt; 1638c2ecf20Sopenharmony_ci inst->alg.decrypt = crypto_pcbc_decrypt; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci err = skcipher_register_instance(tmpl, inst); 1668c2ecf20Sopenharmony_ci if (err) 1678c2ecf20Sopenharmony_ci inst->free(inst); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return err; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic struct crypto_template crypto_pcbc_tmpl = { 1738c2ecf20Sopenharmony_ci .name = "pcbc", 1748c2ecf20Sopenharmony_ci .create = crypto_pcbc_create, 1758c2ecf20Sopenharmony_ci .module = THIS_MODULE, 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int __init crypto_pcbc_module_init(void) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci return crypto_register_template(&crypto_pcbc_tmpl); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void __exit crypto_pcbc_module_exit(void) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci crypto_unregister_template(&crypto_pcbc_tmpl); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cisubsys_initcall(crypto_pcbc_module_init); 1898c2ecf20Sopenharmony_cimodule_exit(crypto_pcbc_module_exit); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1928c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PCBC block cipher mode of operation"); 1938c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("pcbc"); 194