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 #include <linux/slab.h> 24d8c34b94SGideon Israel Dsouza #include <linux/compiler.h> 2591652be5SDavid Howells 2691652be5SDavid Howells struct crypto_pcbc_ctx { 2791652be5SDavid Howells struct crypto_cipher *child; 2891652be5SDavid Howells }; 2991652be5SDavid Howells 30043a4400SHerbert Xu static int crypto_pcbc_setkey(struct crypto_skcipher *parent, const u8 *key, 3191652be5SDavid Howells unsigned int keylen) 3291652be5SDavid Howells { 33043a4400SHerbert Xu struct crypto_pcbc_ctx *ctx = crypto_skcipher_ctx(parent); 3491652be5SDavid Howells struct crypto_cipher *child = ctx->child; 3591652be5SDavid Howells int err; 3691652be5SDavid Howells 3791652be5SDavid Howells crypto_cipher_clear_flags(child, CRYPTO_TFM_REQ_MASK); 38043a4400SHerbert Xu crypto_cipher_set_flags(child, crypto_skcipher_get_flags(parent) & 3991652be5SDavid Howells CRYPTO_TFM_REQ_MASK); 4091652be5SDavid Howells err = crypto_cipher_setkey(child, key, keylen); 41043a4400SHerbert Xu crypto_skcipher_set_flags(parent, crypto_cipher_get_flags(child) & 4291652be5SDavid Howells CRYPTO_TFM_RES_MASK); 4391652be5SDavid Howells return err; 4491652be5SDavid Howells } 4591652be5SDavid Howells 46043a4400SHerbert Xu static int crypto_pcbc_encrypt_segment(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; 5391652be5SDavid Howells u8 *dst = walk->dst.virt.addr; 54*251b7aeaSEric Biggers u8 * const iv = walk->iv; 5591652be5SDavid Howells 5691652be5SDavid Howells do { 57d0b9007aSHerbert Xu crypto_xor(iv, src, bsize); 58043a4400SHerbert Xu crypto_cipher_encrypt_one(tfm, dst, iv); 5945fe93dfSArd Biesheuvel crypto_xor_cpy(iv, dst, src, bsize); 6091652be5SDavid Howells 6191652be5SDavid Howells src += bsize; 6291652be5SDavid Howells dst += bsize; 6391652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 6491652be5SDavid Howells 6591652be5SDavid Howells return nbytes; 6691652be5SDavid Howells } 6791652be5SDavid Howells 68043a4400SHerbert Xu static int crypto_pcbc_encrypt_inplace(struct skcipher_request *req, 69043a4400SHerbert Xu struct skcipher_walk *walk, 70d0b9007aSHerbert Xu struct crypto_cipher *tfm) 7191652be5SDavid Howells { 7291652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 7391652be5SDavid Howells unsigned int nbytes = walk->nbytes; 7491652be5SDavid Howells u8 *src = walk->src.virt.addr; 75*251b7aeaSEric Biggers u8 * const iv = walk->iv; 766650c4deSSalvatore Mesoraca u8 tmpbuf[MAX_CIPHER_BLOCKSIZE]; 7791652be5SDavid Howells 7891652be5SDavid Howells do { 7991652be5SDavid Howells memcpy(tmpbuf, src, bsize); 80d0b9007aSHerbert Xu crypto_xor(iv, src, bsize); 81043a4400SHerbert Xu crypto_cipher_encrypt_one(tfm, src, iv); 8245fe93dfSArd Biesheuvel crypto_xor_cpy(iv, tmpbuf, src, bsize); 8391652be5SDavid Howells 8491652be5SDavid Howells src += bsize; 8591652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 8691652be5SDavid Howells 8791652be5SDavid Howells return nbytes; 8891652be5SDavid Howells } 8991652be5SDavid Howells 90043a4400SHerbert Xu static int crypto_pcbc_encrypt(struct skcipher_request *req) 9191652be5SDavid Howells { 92043a4400SHerbert Xu struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 93043a4400SHerbert Xu struct crypto_pcbc_ctx *ctx = crypto_skcipher_ctx(tfm); 9491652be5SDavid Howells struct crypto_cipher *child = ctx->child; 95043a4400SHerbert Xu struct skcipher_walk walk; 96043a4400SHerbert Xu unsigned int nbytes; 9791652be5SDavid Howells int err; 9891652be5SDavid Howells 99043a4400SHerbert Xu err = skcipher_walk_virt(&walk, req, false); 10091652be5SDavid Howells 10191652be5SDavid Howells while ((nbytes = walk.nbytes)) { 10291652be5SDavid Howells if (walk.src.virt.addr == walk.dst.virt.addr) 103043a4400SHerbert Xu nbytes = crypto_pcbc_encrypt_inplace(req, &walk, 104d0b9007aSHerbert Xu child); 10591652be5SDavid Howells else 106043a4400SHerbert Xu nbytes = crypto_pcbc_encrypt_segment(req, &walk, 107d0b9007aSHerbert Xu child); 108043a4400SHerbert Xu err = skcipher_walk_done(&walk, nbytes); 10991652be5SDavid Howells } 11091652be5SDavid Howells 11191652be5SDavid Howells return err; 11291652be5SDavid Howells } 11391652be5SDavid Howells 114043a4400SHerbert Xu static int crypto_pcbc_decrypt_segment(struct skcipher_request *req, 115043a4400SHerbert Xu struct skcipher_walk *walk, 116d0b9007aSHerbert Xu struct crypto_cipher *tfm) 11791652be5SDavid Howells { 11891652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 11991652be5SDavid Howells unsigned int nbytes = walk->nbytes; 12091652be5SDavid Howells u8 *src = walk->src.virt.addr; 12191652be5SDavid Howells u8 *dst = walk->dst.virt.addr; 122*251b7aeaSEric Biggers u8 * const iv = walk->iv; 12391652be5SDavid Howells 12491652be5SDavid Howells do { 125043a4400SHerbert Xu crypto_cipher_decrypt_one(tfm, dst, src); 126d0b9007aSHerbert Xu crypto_xor(dst, iv, bsize); 12745fe93dfSArd Biesheuvel crypto_xor_cpy(iv, dst, src, bsize); 12891652be5SDavid Howells 12991652be5SDavid Howells src += bsize; 13091652be5SDavid Howells dst += bsize; 13191652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 13291652be5SDavid Howells 13391652be5SDavid Howells return nbytes; 13491652be5SDavid Howells } 13591652be5SDavid Howells 136043a4400SHerbert Xu static int crypto_pcbc_decrypt_inplace(struct skcipher_request *req, 137043a4400SHerbert Xu struct skcipher_walk *walk, 138d0b9007aSHerbert Xu struct crypto_cipher *tfm) 13991652be5SDavid Howells { 14091652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 14191652be5SDavid Howells unsigned int nbytes = walk->nbytes; 14291652be5SDavid Howells u8 *src = walk->src.virt.addr; 143*251b7aeaSEric Biggers u8 * const iv = walk->iv; 1446650c4deSSalvatore Mesoraca u8 tmpbuf[MAX_CIPHER_BLOCKSIZE] __aligned(__alignof__(u32)); 14591652be5SDavid Howells 14691652be5SDavid Howells do { 14791652be5SDavid Howells memcpy(tmpbuf, src, bsize); 148043a4400SHerbert Xu crypto_cipher_decrypt_one(tfm, src, src); 149d0b9007aSHerbert Xu crypto_xor(src, iv, bsize); 15045fe93dfSArd Biesheuvel crypto_xor_cpy(iv, src, tmpbuf, bsize); 15191652be5SDavid Howells 15291652be5SDavid Howells src += bsize; 15391652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 15491652be5SDavid Howells 15591652be5SDavid Howells return nbytes; 15691652be5SDavid Howells } 15791652be5SDavid Howells 158043a4400SHerbert Xu static int crypto_pcbc_decrypt(struct skcipher_request *req) 15991652be5SDavid Howells { 160043a4400SHerbert Xu struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 161043a4400SHerbert Xu struct crypto_pcbc_ctx *ctx = crypto_skcipher_ctx(tfm); 16291652be5SDavid Howells struct crypto_cipher *child = ctx->child; 163043a4400SHerbert Xu struct skcipher_walk walk; 164043a4400SHerbert Xu unsigned int nbytes; 16591652be5SDavid Howells int err; 16691652be5SDavid Howells 167043a4400SHerbert Xu err = skcipher_walk_virt(&walk, req, false); 16891652be5SDavid Howells 16991652be5SDavid Howells while ((nbytes = walk.nbytes)) { 17091652be5SDavid Howells if (walk.src.virt.addr == walk.dst.virt.addr) 171043a4400SHerbert Xu nbytes = crypto_pcbc_decrypt_inplace(req, &walk, 172d0b9007aSHerbert Xu child); 17391652be5SDavid Howells else 174043a4400SHerbert Xu nbytes = crypto_pcbc_decrypt_segment(req, &walk, 175d0b9007aSHerbert Xu child); 176043a4400SHerbert Xu err = skcipher_walk_done(&walk, nbytes); 17791652be5SDavid Howells } 17891652be5SDavid Howells 17991652be5SDavid Howells return err; 18091652be5SDavid Howells } 18191652be5SDavid Howells 182043a4400SHerbert Xu static int crypto_pcbc_init_tfm(struct crypto_skcipher *tfm) 18391652be5SDavid Howells { 184043a4400SHerbert Xu struct skcipher_instance *inst = skcipher_alg_instance(tfm); 185043a4400SHerbert Xu struct crypto_spawn *spawn = skcipher_instance_ctx(inst); 186043a4400SHerbert Xu struct crypto_pcbc_ctx *ctx = crypto_skcipher_ctx(tfm); 1872e306ee0SHerbert Xu struct crypto_cipher *cipher; 18891652be5SDavid Howells 1892e306ee0SHerbert Xu cipher = crypto_spawn_cipher(spawn); 1902e306ee0SHerbert Xu if (IS_ERR(cipher)) 1912e306ee0SHerbert Xu return PTR_ERR(cipher); 19291652be5SDavid Howells 1932e306ee0SHerbert Xu ctx->child = cipher; 19491652be5SDavid Howells return 0; 19591652be5SDavid Howells } 19691652be5SDavid Howells 197043a4400SHerbert Xu static void crypto_pcbc_exit_tfm(struct crypto_skcipher *tfm) 19891652be5SDavid Howells { 199043a4400SHerbert Xu struct crypto_pcbc_ctx *ctx = crypto_skcipher_ctx(tfm); 200043a4400SHerbert Xu 20191652be5SDavid Howells crypto_free_cipher(ctx->child); 20291652be5SDavid Howells } 20391652be5SDavid Howells 204043a4400SHerbert Xu static void crypto_pcbc_free(struct skcipher_instance *inst) 20591652be5SDavid Howells { 206043a4400SHerbert Xu crypto_drop_skcipher(skcipher_instance_ctx(inst)); 207043a4400SHerbert Xu kfree(inst); 208043a4400SHerbert Xu } 209043a4400SHerbert Xu 210043a4400SHerbert Xu static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb) 211043a4400SHerbert Xu { 212043a4400SHerbert Xu struct skcipher_instance *inst; 213043a4400SHerbert Xu struct crypto_attr_type *algt; 214043a4400SHerbert Xu struct crypto_spawn *spawn; 21591652be5SDavid Howells struct crypto_alg *alg; 216ebc610e5SHerbert Xu int err; 21791652be5SDavid Howells 218043a4400SHerbert Xu algt = crypto_get_attr_type(tb); 219043a4400SHerbert Xu if (IS_ERR(algt)) 220043a4400SHerbert Xu return PTR_ERR(algt); 221ebc610e5SHerbert Xu 222043a4400SHerbert Xu if (((algt->type ^ CRYPTO_ALG_TYPE_SKCIPHER) & algt->mask) & 223043a4400SHerbert Xu ~CRYPTO_ALG_INTERNAL) 224043a4400SHerbert Xu return -EINVAL; 225043a4400SHerbert Xu 226043a4400SHerbert Xu inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL); 227043a4400SHerbert Xu if (!inst) 228043a4400SHerbert Xu return -ENOMEM; 229043a4400SHerbert Xu 230043a4400SHerbert Xu alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER | 231043a4400SHerbert Xu (algt->type & CRYPTO_ALG_INTERNAL), 232043a4400SHerbert Xu CRYPTO_ALG_TYPE_MASK | 233043a4400SHerbert Xu (algt->mask & CRYPTO_ALG_INTERNAL)); 234043a4400SHerbert Xu err = PTR_ERR(alg); 23591652be5SDavid Howells if (IS_ERR(alg)) 236043a4400SHerbert Xu goto err_free_inst; 23791652be5SDavid Howells 238043a4400SHerbert Xu spawn = skcipher_instance_ctx(inst); 239043a4400SHerbert Xu err = crypto_init_spawn(spawn, alg, skcipher_crypto_instance(inst), 240043a4400SHerbert Xu CRYPTO_ALG_TYPE_MASK); 241043a4400SHerbert Xu if (err) 242e5bde04cSPan Bian goto err_put_alg; 24391652be5SDavid Howells 244043a4400SHerbert Xu err = crypto_inst_setname(skcipher_crypto_instance(inst), "pcbc", alg); 245043a4400SHerbert Xu if (err) 246043a4400SHerbert Xu goto err_drop_spawn; 247043a4400SHerbert Xu 248043a4400SHerbert Xu inst->alg.base.cra_flags = alg->cra_flags & CRYPTO_ALG_INTERNAL; 249043a4400SHerbert Xu inst->alg.base.cra_priority = alg->cra_priority; 250043a4400SHerbert Xu inst->alg.base.cra_blocksize = alg->cra_blocksize; 251043a4400SHerbert Xu inst->alg.base.cra_alignmask = alg->cra_alignmask; 25291652be5SDavid Howells 253043a4400SHerbert Xu inst->alg.ivsize = alg->cra_blocksize; 254043a4400SHerbert Xu inst->alg.min_keysize = alg->cra_cipher.cia_min_keysize; 255043a4400SHerbert Xu inst->alg.max_keysize = alg->cra_cipher.cia_max_keysize; 25691652be5SDavid Howells 257043a4400SHerbert Xu inst->alg.base.cra_ctxsize = sizeof(struct crypto_pcbc_ctx); 25891652be5SDavid Howells 259043a4400SHerbert Xu inst->alg.init = crypto_pcbc_init_tfm; 260043a4400SHerbert Xu inst->alg.exit = crypto_pcbc_exit_tfm; 26191652be5SDavid Howells 262043a4400SHerbert Xu inst->alg.setkey = crypto_pcbc_setkey; 263043a4400SHerbert Xu inst->alg.encrypt = crypto_pcbc_encrypt; 264043a4400SHerbert Xu inst->alg.decrypt = crypto_pcbc_decrypt; 26591652be5SDavid Howells 266043a4400SHerbert Xu inst->free = crypto_pcbc_free; 26791652be5SDavid Howells 268043a4400SHerbert Xu err = skcipher_register_instance(tmpl, inst); 269043a4400SHerbert Xu if (err) 270043a4400SHerbert Xu goto err_drop_spawn; 271e5bde04cSPan Bian crypto_mod_put(alg); 272043a4400SHerbert Xu 273043a4400SHerbert Xu out: 274043a4400SHerbert Xu return err; 275043a4400SHerbert Xu 276043a4400SHerbert Xu err_drop_spawn: 277043a4400SHerbert Xu crypto_drop_spawn(spawn); 278e5bde04cSPan Bian err_put_alg: 279e5bde04cSPan Bian crypto_mod_put(alg); 280043a4400SHerbert Xu err_free_inst: 28191652be5SDavid Howells kfree(inst); 282043a4400SHerbert Xu goto out; 28391652be5SDavid Howells } 28491652be5SDavid Howells 28591652be5SDavid Howells static struct crypto_template crypto_pcbc_tmpl = { 28691652be5SDavid Howells .name = "pcbc", 287043a4400SHerbert Xu .create = crypto_pcbc_create, 28891652be5SDavid Howells .module = THIS_MODULE, 28991652be5SDavid Howells }; 29091652be5SDavid Howells 29191652be5SDavid Howells static int __init crypto_pcbc_module_init(void) 29291652be5SDavid Howells { 29391652be5SDavid Howells return crypto_register_template(&crypto_pcbc_tmpl); 29491652be5SDavid Howells } 29591652be5SDavid Howells 29691652be5SDavid Howells static void __exit crypto_pcbc_module_exit(void) 29791652be5SDavid Howells { 29891652be5SDavid Howells crypto_unregister_template(&crypto_pcbc_tmpl); 29991652be5SDavid Howells } 30091652be5SDavid Howells 30191652be5SDavid Howells module_init(crypto_pcbc_module_init); 30291652be5SDavid Howells module_exit(crypto_pcbc_module_exit); 30391652be5SDavid Howells 30491652be5SDavid Howells MODULE_LICENSE("GPL"); 30591652be5SDavid Howells MODULE_DESCRIPTION("PCBC block cipher algorithm"); 3064943ba16SKees Cook MODULE_ALIAS_CRYPTO("pcbc"); 307