12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 291652be5SDavid Howells /* 391652be5SDavid Howells * PCBC: Propagating Cipher Block Chaining mode 491652be5SDavid Howells * 591652be5SDavid Howells * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. 691652be5SDavid Howells * Written by David Howells (dhowells@redhat.com) 791652be5SDavid Howells * 891652be5SDavid Howells * Derived from cbc.c 991652be5SDavid Howells * - Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au> 1091652be5SDavid Howells */ 1191652be5SDavid Howells 126650c4deSSalvatore Mesoraca #include <crypto/algapi.h> 13043a4400SHerbert Xu #include <crypto/internal/skcipher.h> 1491652be5SDavid Howells #include <linux/err.h> 1591652be5SDavid Howells #include <linux/init.h> 1691652be5SDavid Howells #include <linux/kernel.h> 1791652be5SDavid Howells #include <linux/module.h> 1891652be5SDavid Howells 19043a4400SHerbert Xu static int crypto_pcbc_encrypt_segment(struct skcipher_request *req, 20043a4400SHerbert Xu struct skcipher_walk *walk, 21d0b9007aSHerbert Xu struct crypto_cipher *tfm) 2291652be5SDavid Howells { 2391652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 2491652be5SDavid Howells unsigned int nbytes = walk->nbytes; 2591652be5SDavid Howells u8 *src = walk->src.virt.addr; 2691652be5SDavid Howells u8 *dst = walk->dst.virt.addr; 27251b7aeaSEric Biggers u8 * const iv = walk->iv; 2891652be5SDavid Howells 2991652be5SDavid Howells do { 30d0b9007aSHerbert Xu crypto_xor(iv, src, bsize); 31043a4400SHerbert Xu crypto_cipher_encrypt_one(tfm, dst, iv); 3245fe93dfSArd Biesheuvel crypto_xor_cpy(iv, dst, src, bsize); 3391652be5SDavid Howells 3491652be5SDavid Howells src += bsize; 3591652be5SDavid Howells dst += bsize; 3691652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 3791652be5SDavid Howells 3891652be5SDavid Howells return nbytes; 3991652be5SDavid Howells } 4091652be5SDavid Howells 41043a4400SHerbert Xu static int crypto_pcbc_encrypt_inplace(struct skcipher_request *req, 42043a4400SHerbert Xu struct skcipher_walk *walk, 43d0b9007aSHerbert Xu struct crypto_cipher *tfm) 4491652be5SDavid Howells { 4591652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 4691652be5SDavid Howells unsigned int nbytes = walk->nbytes; 4791652be5SDavid Howells u8 *src = walk->src.virt.addr; 48251b7aeaSEric Biggers u8 * const iv = walk->iv; 496650c4deSSalvatore Mesoraca u8 tmpbuf[MAX_CIPHER_BLOCKSIZE]; 5091652be5SDavid Howells 5191652be5SDavid Howells do { 5291652be5SDavid Howells memcpy(tmpbuf, src, bsize); 53d0b9007aSHerbert Xu crypto_xor(iv, src, bsize); 54043a4400SHerbert Xu crypto_cipher_encrypt_one(tfm, src, iv); 5545fe93dfSArd Biesheuvel crypto_xor_cpy(iv, tmpbuf, src, bsize); 5691652be5SDavid Howells 5791652be5SDavid Howells src += bsize; 5891652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 5991652be5SDavid Howells 6091652be5SDavid Howells return nbytes; 6191652be5SDavid Howells } 6291652be5SDavid Howells 63043a4400SHerbert Xu static int crypto_pcbc_encrypt(struct skcipher_request *req) 6491652be5SDavid Howells { 65043a4400SHerbert Xu struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 660be487baSEric Biggers struct crypto_cipher *cipher = skcipher_cipher_simple(tfm); 67043a4400SHerbert Xu struct skcipher_walk walk; 68043a4400SHerbert Xu unsigned int nbytes; 6991652be5SDavid Howells int err; 7091652be5SDavid Howells 71043a4400SHerbert Xu err = skcipher_walk_virt(&walk, req, false); 7291652be5SDavid Howells 7391652be5SDavid Howells while ((nbytes = walk.nbytes)) { 7491652be5SDavid Howells if (walk.src.virt.addr == walk.dst.virt.addr) 75043a4400SHerbert Xu nbytes = crypto_pcbc_encrypt_inplace(req, &walk, 760be487baSEric Biggers cipher); 7791652be5SDavid Howells else 78043a4400SHerbert Xu nbytes = crypto_pcbc_encrypt_segment(req, &walk, 790be487baSEric Biggers cipher); 80043a4400SHerbert Xu err = skcipher_walk_done(&walk, nbytes); 8191652be5SDavid Howells } 8291652be5SDavid Howells 8391652be5SDavid Howells return err; 8491652be5SDavid Howells } 8591652be5SDavid Howells 86043a4400SHerbert Xu static int crypto_pcbc_decrypt_segment(struct skcipher_request *req, 87043a4400SHerbert Xu struct skcipher_walk *walk, 88d0b9007aSHerbert Xu struct crypto_cipher *tfm) 8991652be5SDavid Howells { 9091652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 9191652be5SDavid Howells unsigned int nbytes = walk->nbytes; 9291652be5SDavid Howells u8 *src = walk->src.virt.addr; 9391652be5SDavid Howells u8 *dst = walk->dst.virt.addr; 94251b7aeaSEric Biggers u8 * const iv = walk->iv; 9591652be5SDavid Howells 9691652be5SDavid Howells do { 97043a4400SHerbert Xu crypto_cipher_decrypt_one(tfm, dst, src); 98d0b9007aSHerbert Xu crypto_xor(dst, iv, bsize); 9945fe93dfSArd Biesheuvel crypto_xor_cpy(iv, dst, src, bsize); 10091652be5SDavid Howells 10191652be5SDavid Howells src += bsize; 10291652be5SDavid Howells dst += bsize; 10391652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 10491652be5SDavid Howells 10591652be5SDavid Howells return nbytes; 10691652be5SDavid Howells } 10791652be5SDavid Howells 108043a4400SHerbert Xu static int crypto_pcbc_decrypt_inplace(struct skcipher_request *req, 109043a4400SHerbert Xu struct skcipher_walk *walk, 110d0b9007aSHerbert Xu struct crypto_cipher *tfm) 11191652be5SDavid Howells { 11291652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 11391652be5SDavid Howells unsigned int nbytes = walk->nbytes; 11491652be5SDavid Howells u8 *src = walk->src.virt.addr; 115251b7aeaSEric Biggers u8 * const iv = walk->iv; 1166650c4deSSalvatore Mesoraca u8 tmpbuf[MAX_CIPHER_BLOCKSIZE] __aligned(__alignof__(u32)); 11791652be5SDavid Howells 11891652be5SDavid Howells do { 11991652be5SDavid Howells memcpy(tmpbuf, src, bsize); 120043a4400SHerbert Xu crypto_cipher_decrypt_one(tfm, src, src); 121d0b9007aSHerbert Xu crypto_xor(src, iv, bsize); 12245fe93dfSArd Biesheuvel crypto_xor_cpy(iv, src, tmpbuf, bsize); 12391652be5SDavid Howells 12491652be5SDavid Howells src += bsize; 12591652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 12691652be5SDavid Howells 12791652be5SDavid Howells return nbytes; 12891652be5SDavid Howells } 12991652be5SDavid Howells 130043a4400SHerbert Xu static int crypto_pcbc_decrypt(struct skcipher_request *req) 13191652be5SDavid Howells { 132043a4400SHerbert Xu struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 1330be487baSEric Biggers struct crypto_cipher *cipher = skcipher_cipher_simple(tfm); 134043a4400SHerbert Xu struct skcipher_walk walk; 135043a4400SHerbert Xu unsigned int nbytes; 13691652be5SDavid Howells int err; 13791652be5SDavid Howells 138043a4400SHerbert Xu err = skcipher_walk_virt(&walk, req, false); 13991652be5SDavid Howells 14091652be5SDavid Howells while ((nbytes = walk.nbytes)) { 14191652be5SDavid Howells if (walk.src.virt.addr == walk.dst.virt.addr) 142043a4400SHerbert Xu nbytes = crypto_pcbc_decrypt_inplace(req, &walk, 1430be487baSEric Biggers cipher); 14491652be5SDavid Howells else 145043a4400SHerbert Xu nbytes = crypto_pcbc_decrypt_segment(req, &walk, 1460be487baSEric Biggers cipher); 147043a4400SHerbert Xu err = skcipher_walk_done(&walk, nbytes); 14891652be5SDavid Howells } 14991652be5SDavid Howells 15091652be5SDavid Howells return err; 15191652be5SDavid Howells } 15291652be5SDavid Howells 153043a4400SHerbert Xu static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb) 154043a4400SHerbert Xu { 155043a4400SHerbert Xu struct skcipher_instance *inst; 156ebc610e5SHerbert Xu int err; 15791652be5SDavid Howells 158*b3c16bfcSHerbert Xu inst = skcipher_alloc_instance_simple(tmpl, tb); 1590be487baSEric Biggers if (IS_ERR(inst)) 1600be487baSEric Biggers return PTR_ERR(inst); 161ebc610e5SHerbert Xu 162043a4400SHerbert Xu inst->alg.encrypt = crypto_pcbc_encrypt; 163043a4400SHerbert Xu inst->alg.decrypt = crypto_pcbc_decrypt; 16491652be5SDavid Howells 165043a4400SHerbert Xu err = skcipher_register_instance(tmpl, inst); 166043a4400SHerbert Xu if (err) 1670be487baSEric Biggers inst->free(inst); 168*b3c16bfcSHerbert Xu 169043a4400SHerbert Xu return err; 17091652be5SDavid Howells } 17191652be5SDavid Howells 17291652be5SDavid Howells static struct crypto_template crypto_pcbc_tmpl = { 17391652be5SDavid Howells .name = "pcbc", 174043a4400SHerbert Xu .create = crypto_pcbc_create, 17591652be5SDavid Howells .module = THIS_MODULE, 17691652be5SDavid Howells }; 17791652be5SDavid Howells 17891652be5SDavid Howells static int __init crypto_pcbc_module_init(void) 17991652be5SDavid Howells { 18091652be5SDavid Howells return crypto_register_template(&crypto_pcbc_tmpl); 18191652be5SDavid Howells } 18291652be5SDavid Howells 18391652be5SDavid Howells static void __exit crypto_pcbc_module_exit(void) 18491652be5SDavid Howells { 18591652be5SDavid Howells crypto_unregister_template(&crypto_pcbc_tmpl); 18691652be5SDavid Howells } 18791652be5SDavid Howells 188c4741b23SEric Biggers subsys_initcall(crypto_pcbc_module_init); 18991652be5SDavid Howells module_exit(crypto_pcbc_module_exit); 19091652be5SDavid Howells 19191652be5SDavid Howells MODULE_LICENSE("GPL"); 1920be487baSEric Biggers MODULE_DESCRIPTION("PCBC block cipher mode of operation"); 1934943ba16SKees Cook MODULE_ALIAS_CRYPTO("pcbc"); 194