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 17*043a4400SHerbert Xu #include <crypto/internal/skcipher.h> 1891652be5SDavid Howells #include <linux/err.h> 1991652be5SDavid Howells #include <linux/init.h> 2091652be5SDavid Howells #include <linux/kernel.h> 2191652be5SDavid Howells #include <linux/module.h> 2291652be5SDavid Howells #include <linux/slab.h> 2391652be5SDavid Howells 2491652be5SDavid Howells struct crypto_pcbc_ctx { 2591652be5SDavid Howells struct crypto_cipher *child; 2691652be5SDavid Howells }; 2791652be5SDavid Howells 28*043a4400SHerbert Xu static int crypto_pcbc_setkey(struct crypto_skcipher *parent, const u8 *key, 2991652be5SDavid Howells unsigned int keylen) 3091652be5SDavid Howells { 31*043a4400SHerbert Xu struct crypto_pcbc_ctx *ctx = crypto_skcipher_ctx(parent); 3291652be5SDavid Howells struct crypto_cipher *child = ctx->child; 3391652be5SDavid Howells int err; 3491652be5SDavid Howells 3591652be5SDavid Howells crypto_cipher_clear_flags(child, CRYPTO_TFM_REQ_MASK); 36*043a4400SHerbert Xu crypto_cipher_set_flags(child, crypto_skcipher_get_flags(parent) & 3791652be5SDavid Howells CRYPTO_TFM_REQ_MASK); 3891652be5SDavid Howells err = crypto_cipher_setkey(child, key, keylen); 39*043a4400SHerbert Xu crypto_skcipher_set_flags(parent, crypto_cipher_get_flags(child) & 4091652be5SDavid Howells CRYPTO_TFM_RES_MASK); 4191652be5SDavid Howells return err; 4291652be5SDavid Howells } 4391652be5SDavid Howells 44*043a4400SHerbert Xu static int crypto_pcbc_encrypt_segment(struct skcipher_request *req, 45*043a4400SHerbert Xu struct skcipher_walk *walk, 46d0b9007aSHerbert Xu struct crypto_cipher *tfm) 4791652be5SDavid Howells { 4891652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 4991652be5SDavid Howells unsigned int nbytes = walk->nbytes; 5091652be5SDavid Howells u8 *src = walk->src.virt.addr; 5191652be5SDavid Howells u8 *dst = walk->dst.virt.addr; 5291652be5SDavid Howells u8 *iv = walk->iv; 5391652be5SDavid Howells 5491652be5SDavid Howells do { 55d0b9007aSHerbert Xu crypto_xor(iv, src, bsize); 56*043a4400SHerbert Xu crypto_cipher_encrypt_one(tfm, dst, iv); 5791652be5SDavid Howells memcpy(iv, dst, bsize); 58d0b9007aSHerbert Xu crypto_xor(iv, src, bsize); 5991652be5SDavid Howells 6091652be5SDavid Howells src += bsize; 6191652be5SDavid Howells dst += bsize; 6291652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 6391652be5SDavid Howells 6491652be5SDavid Howells return nbytes; 6591652be5SDavid Howells } 6691652be5SDavid Howells 67*043a4400SHerbert Xu static int crypto_pcbc_encrypt_inplace(struct skcipher_request *req, 68*043a4400SHerbert Xu struct skcipher_walk *walk, 69d0b9007aSHerbert Xu struct crypto_cipher *tfm) 7091652be5SDavid Howells { 7191652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 7291652be5SDavid Howells unsigned int nbytes = walk->nbytes; 7391652be5SDavid Howells u8 *src = walk->src.virt.addr; 7491652be5SDavid Howells u8 *iv = walk->iv; 7591652be5SDavid Howells u8 tmpbuf[bsize]; 7691652be5SDavid Howells 7791652be5SDavid Howells do { 7891652be5SDavid Howells memcpy(tmpbuf, src, bsize); 79d0b9007aSHerbert Xu crypto_xor(iv, src, bsize); 80*043a4400SHerbert Xu crypto_cipher_encrypt_one(tfm, src, iv); 81d0b9007aSHerbert Xu memcpy(iv, tmpbuf, bsize); 82d0b9007aSHerbert Xu crypto_xor(iv, src, bsize); 8391652be5SDavid Howells 8491652be5SDavid Howells src += bsize; 8591652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 8691652be5SDavid Howells 8791652be5SDavid Howells memcpy(walk->iv, iv, bsize); 8891652be5SDavid Howells 8991652be5SDavid Howells return nbytes; 9091652be5SDavid Howells } 9191652be5SDavid Howells 92*043a4400SHerbert Xu static int crypto_pcbc_encrypt(struct skcipher_request *req) 9391652be5SDavid Howells { 94*043a4400SHerbert Xu struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 95*043a4400SHerbert Xu struct crypto_pcbc_ctx *ctx = crypto_skcipher_ctx(tfm); 9691652be5SDavid Howells struct crypto_cipher *child = ctx->child; 97*043a4400SHerbert Xu struct skcipher_walk walk; 98*043a4400SHerbert Xu unsigned int nbytes; 9991652be5SDavid Howells int err; 10091652be5SDavid Howells 101*043a4400SHerbert Xu err = skcipher_walk_virt(&walk, req, false); 10291652be5SDavid Howells 10391652be5SDavid Howells while ((nbytes = walk.nbytes)) { 10491652be5SDavid Howells if (walk.src.virt.addr == walk.dst.virt.addr) 105*043a4400SHerbert Xu nbytes = crypto_pcbc_encrypt_inplace(req, &walk, 106d0b9007aSHerbert Xu child); 10791652be5SDavid Howells else 108*043a4400SHerbert Xu nbytes = crypto_pcbc_encrypt_segment(req, &walk, 109d0b9007aSHerbert Xu child); 110*043a4400SHerbert Xu err = skcipher_walk_done(&walk, nbytes); 11191652be5SDavid Howells } 11291652be5SDavid Howells 11391652be5SDavid Howells return err; 11491652be5SDavid Howells } 11591652be5SDavid Howells 116*043a4400SHerbert Xu static int crypto_pcbc_decrypt_segment(struct skcipher_request *req, 117*043a4400SHerbert Xu struct skcipher_walk *walk, 118d0b9007aSHerbert Xu struct crypto_cipher *tfm) 11991652be5SDavid Howells { 12091652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 12191652be5SDavid Howells unsigned int nbytes = walk->nbytes; 12291652be5SDavid Howells u8 *src = walk->src.virt.addr; 12391652be5SDavid Howells u8 *dst = walk->dst.virt.addr; 12491652be5SDavid Howells u8 *iv = walk->iv; 12591652be5SDavid Howells 12691652be5SDavid Howells do { 127*043a4400SHerbert Xu crypto_cipher_decrypt_one(tfm, dst, src); 128d0b9007aSHerbert Xu crypto_xor(dst, iv, bsize); 12991652be5SDavid Howells memcpy(iv, src, bsize); 130d0b9007aSHerbert Xu crypto_xor(iv, dst, bsize); 13191652be5SDavid Howells 13291652be5SDavid Howells src += bsize; 13391652be5SDavid Howells dst += bsize; 13491652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 13591652be5SDavid Howells 13691652be5SDavid Howells memcpy(walk->iv, iv, bsize); 13791652be5SDavid Howells 13891652be5SDavid Howells return nbytes; 13991652be5SDavid Howells } 14091652be5SDavid Howells 141*043a4400SHerbert Xu static int crypto_pcbc_decrypt_inplace(struct skcipher_request *req, 142*043a4400SHerbert Xu struct skcipher_walk *walk, 143d0b9007aSHerbert Xu struct crypto_cipher *tfm) 14491652be5SDavid Howells { 14591652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 14691652be5SDavid Howells unsigned int nbytes = walk->nbytes; 14791652be5SDavid Howells u8 *src = walk->src.virt.addr; 14891652be5SDavid Howells u8 *iv = walk->iv; 149*043a4400SHerbert Xu u8 tmpbuf[bsize] __attribute__ ((aligned(__alignof__(u32)))); 15091652be5SDavid Howells 15191652be5SDavid Howells do { 15291652be5SDavid Howells memcpy(tmpbuf, src, bsize); 153*043a4400SHerbert Xu crypto_cipher_decrypt_one(tfm, src, src); 154d0b9007aSHerbert Xu crypto_xor(src, iv, bsize); 15591652be5SDavid Howells memcpy(iv, tmpbuf, bsize); 156d0b9007aSHerbert Xu crypto_xor(iv, src, bsize); 15791652be5SDavid Howells 15891652be5SDavid Howells src += bsize; 15991652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 16091652be5SDavid Howells 16191652be5SDavid Howells memcpy(walk->iv, iv, bsize); 16291652be5SDavid Howells 16391652be5SDavid Howells return nbytes; 16491652be5SDavid Howells } 16591652be5SDavid Howells 166*043a4400SHerbert Xu static int crypto_pcbc_decrypt(struct skcipher_request *req) 16791652be5SDavid Howells { 168*043a4400SHerbert Xu struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); 169*043a4400SHerbert Xu struct crypto_pcbc_ctx *ctx = crypto_skcipher_ctx(tfm); 17091652be5SDavid Howells struct crypto_cipher *child = ctx->child; 171*043a4400SHerbert Xu struct skcipher_walk walk; 172*043a4400SHerbert Xu unsigned int nbytes; 17391652be5SDavid Howells int err; 17491652be5SDavid Howells 175*043a4400SHerbert Xu err = skcipher_walk_virt(&walk, req, false); 17691652be5SDavid Howells 17791652be5SDavid Howells while ((nbytes = walk.nbytes)) { 17891652be5SDavid Howells if (walk.src.virt.addr == walk.dst.virt.addr) 179*043a4400SHerbert Xu nbytes = crypto_pcbc_decrypt_inplace(req, &walk, 180d0b9007aSHerbert Xu child); 18191652be5SDavid Howells else 182*043a4400SHerbert Xu nbytes = crypto_pcbc_decrypt_segment(req, &walk, 183d0b9007aSHerbert Xu child); 184*043a4400SHerbert Xu err = skcipher_walk_done(&walk, nbytes); 18591652be5SDavid Howells } 18691652be5SDavid Howells 18791652be5SDavid Howells return err; 18891652be5SDavid Howells } 18991652be5SDavid Howells 190*043a4400SHerbert Xu static int crypto_pcbc_init_tfm(struct crypto_skcipher *tfm) 19191652be5SDavid Howells { 192*043a4400SHerbert Xu struct skcipher_instance *inst = skcipher_alg_instance(tfm); 193*043a4400SHerbert Xu struct crypto_spawn *spawn = skcipher_instance_ctx(inst); 194*043a4400SHerbert Xu struct crypto_pcbc_ctx *ctx = crypto_skcipher_ctx(tfm); 1952e306ee0SHerbert Xu struct crypto_cipher *cipher; 19691652be5SDavid Howells 1972e306ee0SHerbert Xu cipher = crypto_spawn_cipher(spawn); 1982e306ee0SHerbert Xu if (IS_ERR(cipher)) 1992e306ee0SHerbert Xu return PTR_ERR(cipher); 20091652be5SDavid Howells 2012e306ee0SHerbert Xu ctx->child = cipher; 20291652be5SDavid Howells return 0; 20391652be5SDavid Howells } 20491652be5SDavid Howells 205*043a4400SHerbert Xu static void crypto_pcbc_exit_tfm(struct crypto_skcipher *tfm) 20691652be5SDavid Howells { 207*043a4400SHerbert Xu struct crypto_pcbc_ctx *ctx = crypto_skcipher_ctx(tfm); 208*043a4400SHerbert Xu 20991652be5SDavid Howells crypto_free_cipher(ctx->child); 21091652be5SDavid Howells } 21191652be5SDavid Howells 212*043a4400SHerbert Xu static void crypto_pcbc_free(struct skcipher_instance *inst) 21391652be5SDavid Howells { 214*043a4400SHerbert Xu crypto_drop_skcipher(skcipher_instance_ctx(inst)); 215*043a4400SHerbert Xu kfree(inst); 216*043a4400SHerbert Xu } 217*043a4400SHerbert Xu 218*043a4400SHerbert Xu static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb) 219*043a4400SHerbert Xu { 220*043a4400SHerbert Xu struct skcipher_instance *inst; 221*043a4400SHerbert Xu struct crypto_attr_type *algt; 222*043a4400SHerbert Xu struct crypto_spawn *spawn; 22391652be5SDavid Howells struct crypto_alg *alg; 224ebc610e5SHerbert Xu int err; 22591652be5SDavid Howells 226*043a4400SHerbert Xu algt = crypto_get_attr_type(tb); 227*043a4400SHerbert Xu if (IS_ERR(algt)) 228*043a4400SHerbert Xu return PTR_ERR(algt); 229ebc610e5SHerbert Xu 230*043a4400SHerbert Xu if (((algt->type ^ CRYPTO_ALG_TYPE_SKCIPHER) & algt->mask) & 231*043a4400SHerbert Xu ~CRYPTO_ALG_INTERNAL) 232*043a4400SHerbert Xu return -EINVAL; 233*043a4400SHerbert Xu 234*043a4400SHerbert Xu inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL); 235*043a4400SHerbert Xu if (!inst) 236*043a4400SHerbert Xu return -ENOMEM; 237*043a4400SHerbert Xu 238*043a4400SHerbert Xu alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER | 239*043a4400SHerbert Xu (algt->type & CRYPTO_ALG_INTERNAL), 240*043a4400SHerbert Xu CRYPTO_ALG_TYPE_MASK | 241*043a4400SHerbert Xu (algt->mask & CRYPTO_ALG_INTERNAL)); 242*043a4400SHerbert Xu err = PTR_ERR(alg); 24391652be5SDavid Howells if (IS_ERR(alg)) 244*043a4400SHerbert Xu goto err_free_inst; 24591652be5SDavid Howells 246*043a4400SHerbert Xu spawn = skcipher_instance_ctx(inst); 247*043a4400SHerbert Xu err = crypto_init_spawn(spawn, alg, skcipher_crypto_instance(inst), 248*043a4400SHerbert Xu CRYPTO_ALG_TYPE_MASK); 249*043a4400SHerbert Xu crypto_mod_put(alg); 250*043a4400SHerbert Xu if (err) 251*043a4400SHerbert Xu goto err_free_inst; 25291652be5SDavid Howells 253*043a4400SHerbert Xu err = crypto_inst_setname(skcipher_crypto_instance(inst), "pcbc", alg); 254*043a4400SHerbert Xu if (err) 255*043a4400SHerbert Xu goto err_drop_spawn; 256*043a4400SHerbert Xu 257*043a4400SHerbert Xu inst->alg.base.cra_flags = alg->cra_flags & CRYPTO_ALG_INTERNAL; 258*043a4400SHerbert Xu inst->alg.base.cra_priority = alg->cra_priority; 259*043a4400SHerbert Xu inst->alg.base.cra_blocksize = alg->cra_blocksize; 260*043a4400SHerbert Xu inst->alg.base.cra_alignmask = alg->cra_alignmask; 26191652be5SDavid Howells 262d0b9007aSHerbert Xu /* We access the data as u32s when xoring. */ 263*043a4400SHerbert Xu inst->alg.base.cra_alignmask |= __alignof__(u32) - 1; 264d0b9007aSHerbert Xu 265*043a4400SHerbert Xu inst->alg.ivsize = alg->cra_blocksize; 266*043a4400SHerbert Xu inst->alg.min_keysize = alg->cra_cipher.cia_min_keysize; 267*043a4400SHerbert Xu inst->alg.max_keysize = alg->cra_cipher.cia_max_keysize; 26891652be5SDavid Howells 269*043a4400SHerbert Xu inst->alg.base.cra_ctxsize = sizeof(struct crypto_pcbc_ctx); 27091652be5SDavid Howells 271*043a4400SHerbert Xu inst->alg.init = crypto_pcbc_init_tfm; 272*043a4400SHerbert Xu inst->alg.exit = crypto_pcbc_exit_tfm; 27391652be5SDavid Howells 274*043a4400SHerbert Xu inst->alg.setkey = crypto_pcbc_setkey; 275*043a4400SHerbert Xu inst->alg.encrypt = crypto_pcbc_encrypt; 276*043a4400SHerbert Xu inst->alg.decrypt = crypto_pcbc_decrypt; 27791652be5SDavid Howells 278*043a4400SHerbert Xu inst->free = crypto_pcbc_free; 27991652be5SDavid Howells 280*043a4400SHerbert Xu err = skcipher_register_instance(tmpl, inst); 281*043a4400SHerbert Xu if (err) 282*043a4400SHerbert Xu goto err_drop_spawn; 283*043a4400SHerbert Xu 284*043a4400SHerbert Xu out: 285*043a4400SHerbert Xu return err; 286*043a4400SHerbert Xu 287*043a4400SHerbert Xu err_drop_spawn: 288*043a4400SHerbert Xu crypto_drop_spawn(spawn); 289*043a4400SHerbert Xu err_free_inst: 29091652be5SDavid Howells kfree(inst); 291*043a4400SHerbert Xu goto out; 29291652be5SDavid Howells } 29391652be5SDavid Howells 29491652be5SDavid Howells static struct crypto_template crypto_pcbc_tmpl = { 29591652be5SDavid Howells .name = "pcbc", 296*043a4400SHerbert Xu .create = crypto_pcbc_create, 29791652be5SDavid Howells .module = THIS_MODULE, 29891652be5SDavid Howells }; 29991652be5SDavid Howells 30091652be5SDavid Howells static int __init crypto_pcbc_module_init(void) 30191652be5SDavid Howells { 30291652be5SDavid Howells return crypto_register_template(&crypto_pcbc_tmpl); 30391652be5SDavid Howells } 30491652be5SDavid Howells 30591652be5SDavid Howells static void __exit crypto_pcbc_module_exit(void) 30691652be5SDavid Howells { 30791652be5SDavid Howells crypto_unregister_template(&crypto_pcbc_tmpl); 30891652be5SDavid Howells } 30991652be5SDavid Howells 31091652be5SDavid Howells module_init(crypto_pcbc_module_init); 31191652be5SDavid Howells module_exit(crypto_pcbc_module_exit); 31291652be5SDavid Howells 31391652be5SDavid Howells MODULE_LICENSE("GPL"); 31491652be5SDavid Howells MODULE_DESCRIPTION("PCBC block cipher algorithm"); 3154943ba16SKees Cook MODULE_ALIAS_CRYPTO("pcbc"); 316