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> 13*0eb76ba2SArd Biesheuvel #include <crypto/internal/cipher.h> 14043a4400SHerbert Xu #include <crypto/internal/skcipher.h> 1591652be5SDavid Howells #include <linux/err.h> 1691652be5SDavid Howells #include <linux/init.h> 1791652be5SDavid Howells #include <linux/kernel.h> 1891652be5SDavid Howells #include <linux/module.h> 1991652be5SDavid Howells 20043a4400SHerbert Xu static int crypto_pcbc_encrypt_segment(struct skcipher_request *req, 21043a4400SHerbert Xu struct skcipher_walk *walk, 22d0b9007aSHerbert Xu struct crypto_cipher *tfm) 2391652be5SDavid Howells { 2491652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 2591652be5SDavid Howells unsigned int nbytes = walk->nbytes; 2691652be5SDavid Howells u8 *src = walk->src.virt.addr; 2791652be5SDavid Howells u8 *dst = walk->dst.virt.addr; 28251b7aeaSEric Biggers u8 * const iv = walk->iv; 2991652be5SDavid Howells 3091652be5SDavid Howells do { 31d0b9007aSHerbert Xu crypto_xor(iv, src, bsize); 32043a4400SHerbert Xu crypto_cipher_encrypt_one(tfm, dst, iv); 3345fe93dfSArd Biesheuvel crypto_xor_cpy(iv, dst, src, bsize); 3491652be5SDavid Howells 3591652be5SDavid Howells src += bsize; 3691652be5SDavid Howells dst += bsize; 3791652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 3891652be5SDavid Howells 3991652be5SDavid Howells return nbytes; 4091652be5SDavid Howells } 4191652be5SDavid Howells 42043a4400SHerbert Xu static int crypto_pcbc_encrypt_inplace(struct skcipher_request *req, 43043a4400SHerbert Xu struct skcipher_walk *walk, 44d0b9007aSHerbert Xu struct crypto_cipher *tfm) 4591652be5SDavid Howells { 4691652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 4791652be5SDavid Howells unsigned int nbytes = walk->nbytes; 4891652be5SDavid Howells u8 *src = walk->src.virt.addr; 49251b7aeaSEric Biggers u8 * const iv = walk->iv; 506650c4deSSalvatore Mesoraca u8 tmpbuf[MAX_CIPHER_BLOCKSIZE]; 5191652be5SDavid Howells 5291652be5SDavid Howells do { 5391652be5SDavid Howells memcpy(tmpbuf, src, bsize); 54d0b9007aSHerbert Xu crypto_xor(iv, src, bsize); 55043a4400SHerbert Xu crypto_cipher_encrypt_one(tfm, src, iv); 5645fe93dfSArd Biesheuvel crypto_xor_cpy(iv, tmpbuf, src, bsize); 5791652be5SDavid Howells 5891652be5SDavid Howells src += bsize; 5991652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 6091652be5SDavid Howells 6191652be5SDavid Howells return nbytes; 6291652be5SDavid Howells } 6391652be5SDavid Howells 64043a4400SHerbert Xu static int crypto_pcbc_encrypt(struct skcipher_request *req) 6591652be5SDavid Howells { 66043a4400SHerbert Xu struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 670be487baSEric Biggers struct crypto_cipher *cipher = skcipher_cipher_simple(tfm); 68043a4400SHerbert Xu struct skcipher_walk walk; 69043a4400SHerbert Xu unsigned int nbytes; 7091652be5SDavid Howells int err; 7191652be5SDavid Howells 72043a4400SHerbert Xu err = skcipher_walk_virt(&walk, req, false); 7391652be5SDavid Howells 7491652be5SDavid Howells while ((nbytes = walk.nbytes)) { 7591652be5SDavid Howells if (walk.src.virt.addr == walk.dst.virt.addr) 76043a4400SHerbert Xu nbytes = crypto_pcbc_encrypt_inplace(req, &walk, 770be487baSEric Biggers cipher); 7891652be5SDavid Howells else 79043a4400SHerbert Xu nbytes = crypto_pcbc_encrypt_segment(req, &walk, 800be487baSEric Biggers cipher); 81043a4400SHerbert Xu err = skcipher_walk_done(&walk, nbytes); 8291652be5SDavid Howells } 8391652be5SDavid Howells 8491652be5SDavid Howells return err; 8591652be5SDavid Howells } 8691652be5SDavid Howells 87043a4400SHerbert Xu static int crypto_pcbc_decrypt_segment(struct skcipher_request *req, 88043a4400SHerbert Xu struct skcipher_walk *walk, 89d0b9007aSHerbert Xu struct crypto_cipher *tfm) 9091652be5SDavid Howells { 9191652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 9291652be5SDavid Howells unsigned int nbytes = walk->nbytes; 9391652be5SDavid Howells u8 *src = walk->src.virt.addr; 9491652be5SDavid Howells u8 *dst = walk->dst.virt.addr; 95251b7aeaSEric Biggers u8 * const iv = walk->iv; 9691652be5SDavid Howells 9791652be5SDavid Howells do { 98043a4400SHerbert Xu crypto_cipher_decrypt_one(tfm, dst, src); 99d0b9007aSHerbert Xu crypto_xor(dst, iv, bsize); 10045fe93dfSArd Biesheuvel crypto_xor_cpy(iv, dst, src, bsize); 10191652be5SDavid Howells 10291652be5SDavid Howells src += bsize; 10391652be5SDavid Howells dst += bsize; 10491652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 10591652be5SDavid Howells 10691652be5SDavid Howells return nbytes; 10791652be5SDavid Howells } 10891652be5SDavid Howells 109043a4400SHerbert Xu static int crypto_pcbc_decrypt_inplace(struct skcipher_request *req, 110043a4400SHerbert Xu struct skcipher_walk *walk, 111d0b9007aSHerbert Xu struct crypto_cipher *tfm) 11291652be5SDavid Howells { 11391652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 11491652be5SDavid Howells unsigned int nbytes = walk->nbytes; 11591652be5SDavid Howells u8 *src = walk->src.virt.addr; 116251b7aeaSEric Biggers u8 * const iv = walk->iv; 1176650c4deSSalvatore Mesoraca u8 tmpbuf[MAX_CIPHER_BLOCKSIZE] __aligned(__alignof__(u32)); 11891652be5SDavid Howells 11991652be5SDavid Howells do { 12091652be5SDavid Howells memcpy(tmpbuf, src, bsize); 121043a4400SHerbert Xu crypto_cipher_decrypt_one(tfm, src, src); 122d0b9007aSHerbert Xu crypto_xor(src, iv, bsize); 12345fe93dfSArd Biesheuvel crypto_xor_cpy(iv, src, tmpbuf, bsize); 12491652be5SDavid Howells 12591652be5SDavid Howells src += bsize; 12691652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 12791652be5SDavid Howells 12891652be5SDavid Howells return nbytes; 12991652be5SDavid Howells } 13091652be5SDavid Howells 131043a4400SHerbert Xu static int crypto_pcbc_decrypt(struct skcipher_request *req) 13291652be5SDavid Howells { 133043a4400SHerbert Xu struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 1340be487baSEric Biggers struct crypto_cipher *cipher = skcipher_cipher_simple(tfm); 135043a4400SHerbert Xu struct skcipher_walk walk; 136043a4400SHerbert Xu unsigned int nbytes; 13791652be5SDavid Howells int err; 13891652be5SDavid Howells 139043a4400SHerbert Xu err = skcipher_walk_virt(&walk, req, false); 14091652be5SDavid Howells 14191652be5SDavid Howells while ((nbytes = walk.nbytes)) { 14291652be5SDavid Howells if (walk.src.virt.addr == walk.dst.virt.addr) 143043a4400SHerbert Xu nbytes = crypto_pcbc_decrypt_inplace(req, &walk, 1440be487baSEric Biggers cipher); 14591652be5SDavid Howells else 146043a4400SHerbert Xu nbytes = crypto_pcbc_decrypt_segment(req, &walk, 1470be487baSEric Biggers cipher); 148043a4400SHerbert Xu err = skcipher_walk_done(&walk, nbytes); 14991652be5SDavid Howells } 15091652be5SDavid Howells 15191652be5SDavid Howells return err; 15291652be5SDavid Howells } 15391652be5SDavid Howells 154043a4400SHerbert Xu static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb) 155043a4400SHerbert Xu { 156043a4400SHerbert Xu struct skcipher_instance *inst; 157ebc610e5SHerbert Xu int err; 15891652be5SDavid Howells 159b3c16bfcSHerbert Xu inst = skcipher_alloc_instance_simple(tmpl, tb); 1600be487baSEric Biggers if (IS_ERR(inst)) 1610be487baSEric Biggers return PTR_ERR(inst); 162ebc610e5SHerbert Xu 163043a4400SHerbert Xu inst->alg.encrypt = crypto_pcbc_encrypt; 164043a4400SHerbert Xu inst->alg.decrypt = crypto_pcbc_decrypt; 16591652be5SDavid Howells 166043a4400SHerbert Xu err = skcipher_register_instance(tmpl, inst); 167043a4400SHerbert Xu if (err) 1680be487baSEric Biggers inst->free(inst); 169b3c16bfcSHerbert Xu 170043a4400SHerbert Xu return err; 17191652be5SDavid Howells } 17291652be5SDavid Howells 17391652be5SDavid Howells static struct crypto_template crypto_pcbc_tmpl = { 17491652be5SDavid Howells .name = "pcbc", 175043a4400SHerbert Xu .create = crypto_pcbc_create, 17691652be5SDavid Howells .module = THIS_MODULE, 17791652be5SDavid Howells }; 17891652be5SDavid Howells 17991652be5SDavid Howells static int __init crypto_pcbc_module_init(void) 18091652be5SDavid Howells { 18191652be5SDavid Howells return crypto_register_template(&crypto_pcbc_tmpl); 18291652be5SDavid Howells } 18391652be5SDavid Howells 18491652be5SDavid Howells static void __exit crypto_pcbc_module_exit(void) 18591652be5SDavid Howells { 18691652be5SDavid Howells crypto_unregister_template(&crypto_pcbc_tmpl); 18791652be5SDavid Howells } 18891652be5SDavid Howells 189c4741b23SEric Biggers subsys_initcall(crypto_pcbc_module_init); 19091652be5SDavid Howells module_exit(crypto_pcbc_module_exit); 19191652be5SDavid Howells 19291652be5SDavid Howells MODULE_LICENSE("GPL"); 1930be487baSEric Biggers MODULE_DESCRIPTION("PCBC block cipher mode of operation"); 1944943ba16SKees Cook MODULE_ALIAS_CRYPTO("pcbc"); 195*0eb76ba2SArd Biesheuvel MODULE_IMPORT_NS(CRYPTO_INTERNAL); 196