191652be5SDavid Howells /* 291652be5SDavid Howells * PCBC: Propagating Cipher Block Chaining mode 391652be5SDavid Howells * 491652be5SDavid Howells * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. 591652be5SDavid Howells * Written by David Howells (dhowells@redhat.com) 691652be5SDavid Howells * 791652be5SDavid Howells * Derived from cbc.c 891652be5SDavid Howells * - Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au> 991652be5SDavid Howells * 1091652be5SDavid Howells * This program is free software; you can redistribute it and/or modify it 1191652be5SDavid Howells * under the terms of the GNU General Public License as published by the Free 1291652be5SDavid Howells * Software Foundation; either version 2 of the License, or (at your option) 1391652be5SDavid Howells * any later version. 1491652be5SDavid Howells * 1591652be5SDavid Howells */ 1691652be5SDavid Howells 176650c4deSSalvatore Mesoraca #include <crypto/algapi.h> 18043a4400SHerbert Xu #include <crypto/internal/skcipher.h> 1991652be5SDavid Howells #include <linux/err.h> 2091652be5SDavid Howells #include <linux/init.h> 2191652be5SDavid Howells #include <linux/kernel.h> 2291652be5SDavid Howells #include <linux/module.h> 2391652be5SDavid Howells 24043a4400SHerbert Xu static int crypto_pcbc_encrypt_segment(struct skcipher_request *req, 25043a4400SHerbert Xu struct skcipher_walk *walk, 26d0b9007aSHerbert Xu struct crypto_cipher *tfm) 2791652be5SDavid Howells { 2891652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 2991652be5SDavid Howells unsigned int nbytes = walk->nbytes; 3091652be5SDavid Howells u8 *src = walk->src.virt.addr; 3191652be5SDavid Howells u8 *dst = walk->dst.virt.addr; 32251b7aeaSEric Biggers u8 * const iv = walk->iv; 3391652be5SDavid Howells 3491652be5SDavid Howells do { 35d0b9007aSHerbert Xu crypto_xor(iv, src, bsize); 36043a4400SHerbert Xu crypto_cipher_encrypt_one(tfm, dst, iv); 3745fe93dfSArd Biesheuvel crypto_xor_cpy(iv, dst, src, bsize); 3891652be5SDavid Howells 3991652be5SDavid Howells src += bsize; 4091652be5SDavid Howells dst += bsize; 4191652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 4291652be5SDavid Howells 4391652be5SDavid Howells return nbytes; 4491652be5SDavid Howells } 4591652be5SDavid Howells 46043a4400SHerbert Xu static int crypto_pcbc_encrypt_inplace(struct skcipher_request *req, 47043a4400SHerbert Xu struct skcipher_walk *walk, 48d0b9007aSHerbert Xu struct crypto_cipher *tfm) 4991652be5SDavid Howells { 5091652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 5191652be5SDavid Howells unsigned int nbytes = walk->nbytes; 5291652be5SDavid Howells u8 *src = walk->src.virt.addr; 53251b7aeaSEric Biggers u8 * const iv = walk->iv; 546650c4deSSalvatore Mesoraca u8 tmpbuf[MAX_CIPHER_BLOCKSIZE]; 5591652be5SDavid Howells 5691652be5SDavid Howells do { 5791652be5SDavid Howells memcpy(tmpbuf, src, bsize); 58d0b9007aSHerbert Xu crypto_xor(iv, src, bsize); 59043a4400SHerbert Xu crypto_cipher_encrypt_one(tfm, src, iv); 6045fe93dfSArd Biesheuvel crypto_xor_cpy(iv, tmpbuf, src, bsize); 6191652be5SDavid Howells 6291652be5SDavid Howells src += bsize; 6391652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 6491652be5SDavid Howells 6591652be5SDavid Howells return nbytes; 6691652be5SDavid Howells } 6791652be5SDavid Howells 68043a4400SHerbert Xu static int crypto_pcbc_encrypt(struct skcipher_request *req) 6991652be5SDavid Howells { 70043a4400SHerbert Xu struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 71*0be487baSEric Biggers struct crypto_cipher *cipher = skcipher_cipher_simple(tfm); 72043a4400SHerbert Xu struct skcipher_walk walk; 73043a4400SHerbert Xu unsigned int nbytes; 7491652be5SDavid Howells int err; 7591652be5SDavid Howells 76043a4400SHerbert Xu err = skcipher_walk_virt(&walk, req, false); 7791652be5SDavid Howells 7891652be5SDavid Howells while ((nbytes = walk.nbytes)) { 7991652be5SDavid Howells if (walk.src.virt.addr == walk.dst.virt.addr) 80043a4400SHerbert Xu nbytes = crypto_pcbc_encrypt_inplace(req, &walk, 81*0be487baSEric Biggers cipher); 8291652be5SDavid Howells else 83043a4400SHerbert Xu nbytes = crypto_pcbc_encrypt_segment(req, &walk, 84*0be487baSEric Biggers cipher); 85043a4400SHerbert Xu err = skcipher_walk_done(&walk, nbytes); 8691652be5SDavid Howells } 8791652be5SDavid Howells 8891652be5SDavid Howells return err; 8991652be5SDavid Howells } 9091652be5SDavid Howells 91043a4400SHerbert Xu static int crypto_pcbc_decrypt_segment(struct skcipher_request *req, 92043a4400SHerbert Xu struct skcipher_walk *walk, 93d0b9007aSHerbert Xu struct crypto_cipher *tfm) 9491652be5SDavid Howells { 9591652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 9691652be5SDavid Howells unsigned int nbytes = walk->nbytes; 9791652be5SDavid Howells u8 *src = walk->src.virt.addr; 9891652be5SDavid Howells u8 *dst = walk->dst.virt.addr; 99251b7aeaSEric Biggers u8 * const iv = walk->iv; 10091652be5SDavid Howells 10191652be5SDavid Howells do { 102043a4400SHerbert Xu crypto_cipher_decrypt_one(tfm, dst, src); 103d0b9007aSHerbert Xu crypto_xor(dst, iv, bsize); 10445fe93dfSArd Biesheuvel crypto_xor_cpy(iv, dst, src, bsize); 10591652be5SDavid Howells 10691652be5SDavid Howells src += bsize; 10791652be5SDavid Howells dst += bsize; 10891652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 10991652be5SDavid Howells 11091652be5SDavid Howells return nbytes; 11191652be5SDavid Howells } 11291652be5SDavid Howells 113043a4400SHerbert Xu static int crypto_pcbc_decrypt_inplace(struct skcipher_request *req, 114043a4400SHerbert Xu struct skcipher_walk *walk, 115d0b9007aSHerbert Xu struct crypto_cipher *tfm) 11691652be5SDavid Howells { 11791652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 11891652be5SDavid Howells unsigned int nbytes = walk->nbytes; 11991652be5SDavid Howells u8 *src = walk->src.virt.addr; 120251b7aeaSEric Biggers u8 * const iv = walk->iv; 1216650c4deSSalvatore Mesoraca u8 tmpbuf[MAX_CIPHER_BLOCKSIZE] __aligned(__alignof__(u32)); 12291652be5SDavid Howells 12391652be5SDavid Howells do { 12491652be5SDavid Howells memcpy(tmpbuf, src, bsize); 125043a4400SHerbert Xu crypto_cipher_decrypt_one(tfm, src, src); 126d0b9007aSHerbert Xu crypto_xor(src, iv, bsize); 12745fe93dfSArd Biesheuvel crypto_xor_cpy(iv, src, tmpbuf, bsize); 12891652be5SDavid Howells 12991652be5SDavid Howells src += bsize; 13091652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 13191652be5SDavid Howells 13291652be5SDavid Howells return nbytes; 13391652be5SDavid Howells } 13491652be5SDavid Howells 135043a4400SHerbert Xu static int crypto_pcbc_decrypt(struct skcipher_request *req) 13691652be5SDavid Howells { 137043a4400SHerbert Xu struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 138*0be487baSEric Biggers struct crypto_cipher *cipher = skcipher_cipher_simple(tfm); 139043a4400SHerbert Xu struct skcipher_walk walk; 140043a4400SHerbert Xu unsigned int nbytes; 14191652be5SDavid Howells int err; 14291652be5SDavid Howells 143043a4400SHerbert Xu err = skcipher_walk_virt(&walk, req, false); 14491652be5SDavid Howells 14591652be5SDavid Howells while ((nbytes = walk.nbytes)) { 14691652be5SDavid Howells if (walk.src.virt.addr == walk.dst.virt.addr) 147043a4400SHerbert Xu nbytes = crypto_pcbc_decrypt_inplace(req, &walk, 148*0be487baSEric Biggers cipher); 14991652be5SDavid Howells else 150043a4400SHerbert Xu nbytes = crypto_pcbc_decrypt_segment(req, &walk, 151*0be487baSEric Biggers cipher); 152043a4400SHerbert Xu err = skcipher_walk_done(&walk, nbytes); 15391652be5SDavid Howells } 15491652be5SDavid Howells 15591652be5SDavid Howells return err; 15691652be5SDavid Howells } 15791652be5SDavid Howells 158043a4400SHerbert Xu static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb) 159043a4400SHerbert Xu { 160043a4400SHerbert Xu struct skcipher_instance *inst; 16191652be5SDavid Howells struct crypto_alg *alg; 162ebc610e5SHerbert Xu int err; 16391652be5SDavid Howells 164*0be487baSEric Biggers inst = skcipher_alloc_instance_simple(tmpl, tb, &alg); 165*0be487baSEric Biggers if (IS_ERR(inst)) 166*0be487baSEric Biggers return PTR_ERR(inst); 167ebc610e5SHerbert Xu 168043a4400SHerbert Xu inst->alg.encrypt = crypto_pcbc_encrypt; 169043a4400SHerbert Xu inst->alg.decrypt = crypto_pcbc_decrypt; 17091652be5SDavid Howells 171043a4400SHerbert Xu err = skcipher_register_instance(tmpl, inst); 172043a4400SHerbert Xu if (err) 173*0be487baSEric Biggers inst->free(inst); 174e5bde04cSPan Bian crypto_mod_put(alg); 175043a4400SHerbert Xu return err; 17691652be5SDavid Howells } 17791652be5SDavid Howells 17891652be5SDavid Howells static struct crypto_template crypto_pcbc_tmpl = { 17991652be5SDavid Howells .name = "pcbc", 180043a4400SHerbert Xu .create = crypto_pcbc_create, 18191652be5SDavid Howells .module = THIS_MODULE, 18291652be5SDavid Howells }; 18391652be5SDavid Howells 18491652be5SDavid Howells static int __init crypto_pcbc_module_init(void) 18591652be5SDavid Howells { 18691652be5SDavid Howells return crypto_register_template(&crypto_pcbc_tmpl); 18791652be5SDavid Howells } 18891652be5SDavid Howells 18991652be5SDavid Howells static void __exit crypto_pcbc_module_exit(void) 19091652be5SDavid Howells { 19191652be5SDavid Howells crypto_unregister_template(&crypto_pcbc_tmpl); 19291652be5SDavid Howells } 19391652be5SDavid Howells 19491652be5SDavid Howells module_init(crypto_pcbc_module_init); 19591652be5SDavid Howells module_exit(crypto_pcbc_module_exit); 19691652be5SDavid Howells 19791652be5SDavid Howells MODULE_LICENSE("GPL"); 198*0be487baSEric Biggers MODULE_DESCRIPTION("PCBC block cipher mode of operation"); 1994943ba16SKees Cook MODULE_ALIAS_CRYPTO("pcbc"); 200