1*91652be5SDavid Howells /* 2*91652be5SDavid Howells * PCBC: Propagating Cipher Block Chaining mode 3*91652be5SDavid Howells * 4*91652be5SDavid Howells * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. 5*91652be5SDavid Howells * Written by David Howells (dhowells@redhat.com) 6*91652be5SDavid Howells * 7*91652be5SDavid Howells * Derived from cbc.c 8*91652be5SDavid Howells * - Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au> 9*91652be5SDavid Howells * 10*91652be5SDavid Howells * This program is free software; you can redistribute it and/or modify it 11*91652be5SDavid Howells * under the terms of the GNU General Public License as published by the Free 12*91652be5SDavid Howells * Software Foundation; either version 2 of the License, or (at your option) 13*91652be5SDavid Howells * any later version. 14*91652be5SDavid Howells * 15*91652be5SDavid Howells */ 16*91652be5SDavid Howells 17*91652be5SDavid Howells #include <crypto/algapi.h> 18*91652be5SDavid Howells #include <linux/err.h> 19*91652be5SDavid Howells #include <linux/init.h> 20*91652be5SDavid Howells #include <linux/kernel.h> 21*91652be5SDavid Howells #include <linux/module.h> 22*91652be5SDavid Howells #include <linux/scatterlist.h> 23*91652be5SDavid Howells #include <linux/slab.h> 24*91652be5SDavid Howells 25*91652be5SDavid Howells struct crypto_pcbc_ctx { 26*91652be5SDavid Howells struct crypto_cipher *child; 27*91652be5SDavid Howells void (*xor)(u8 *dst, const u8 *src, unsigned int bs); 28*91652be5SDavid Howells }; 29*91652be5SDavid Howells 30*91652be5SDavid Howells static int crypto_pcbc_setkey(struct crypto_tfm *parent, const u8 *key, 31*91652be5SDavid Howells unsigned int keylen) 32*91652be5SDavid Howells { 33*91652be5SDavid Howells struct crypto_pcbc_ctx *ctx = crypto_tfm_ctx(parent); 34*91652be5SDavid Howells struct crypto_cipher *child = ctx->child; 35*91652be5SDavid Howells int err; 36*91652be5SDavid Howells 37*91652be5SDavid Howells crypto_cipher_clear_flags(child, CRYPTO_TFM_REQ_MASK); 38*91652be5SDavid Howells crypto_cipher_set_flags(child, crypto_tfm_get_flags(parent) & 39*91652be5SDavid Howells CRYPTO_TFM_REQ_MASK); 40*91652be5SDavid Howells err = crypto_cipher_setkey(child, key, keylen); 41*91652be5SDavid Howells crypto_tfm_set_flags(parent, crypto_cipher_get_flags(child) & 42*91652be5SDavid Howells CRYPTO_TFM_RES_MASK); 43*91652be5SDavid Howells return err; 44*91652be5SDavid Howells } 45*91652be5SDavid Howells 46*91652be5SDavid Howells static int crypto_pcbc_encrypt_segment(struct blkcipher_desc *desc, 47*91652be5SDavid Howells struct blkcipher_walk *walk, 48*91652be5SDavid Howells struct crypto_cipher *tfm, 49*91652be5SDavid Howells void (*xor)(u8 *, const u8 *, 50*91652be5SDavid Howells unsigned int)) 51*91652be5SDavid Howells { 52*91652be5SDavid Howells void (*fn)(struct crypto_tfm *, u8 *, const u8 *) = 53*91652be5SDavid Howells crypto_cipher_alg(tfm)->cia_encrypt; 54*91652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 55*91652be5SDavid Howells unsigned int nbytes = walk->nbytes; 56*91652be5SDavid Howells u8 *src = walk->src.virt.addr; 57*91652be5SDavid Howells u8 *dst = walk->dst.virt.addr; 58*91652be5SDavid Howells u8 *iv = walk->iv; 59*91652be5SDavid Howells 60*91652be5SDavid Howells do { 61*91652be5SDavid Howells xor(iv, src, bsize); 62*91652be5SDavid Howells fn(crypto_cipher_tfm(tfm), dst, iv); 63*91652be5SDavid Howells memcpy(iv, dst, bsize); 64*91652be5SDavid Howells xor(iv, src, bsize); 65*91652be5SDavid Howells 66*91652be5SDavid Howells src += bsize; 67*91652be5SDavid Howells dst += bsize; 68*91652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 69*91652be5SDavid Howells 70*91652be5SDavid Howells return nbytes; 71*91652be5SDavid Howells } 72*91652be5SDavid Howells 73*91652be5SDavid Howells static int crypto_pcbc_encrypt_inplace(struct blkcipher_desc *desc, 74*91652be5SDavid Howells struct blkcipher_walk *walk, 75*91652be5SDavid Howells struct crypto_cipher *tfm, 76*91652be5SDavid Howells void (*xor)(u8 *, const u8 *, 77*91652be5SDavid Howells unsigned int)) 78*91652be5SDavid Howells { 79*91652be5SDavid Howells void (*fn)(struct crypto_tfm *, u8 *, const u8 *) = 80*91652be5SDavid Howells crypto_cipher_alg(tfm)->cia_encrypt; 81*91652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 82*91652be5SDavid Howells unsigned int nbytes = walk->nbytes; 83*91652be5SDavid Howells u8 *src = walk->src.virt.addr; 84*91652be5SDavid Howells u8 *iv = walk->iv; 85*91652be5SDavid Howells u8 tmpbuf[bsize]; 86*91652be5SDavid Howells 87*91652be5SDavid Howells do { 88*91652be5SDavid Howells memcpy(tmpbuf, src, bsize); 89*91652be5SDavid Howells xor(iv, tmpbuf, bsize); 90*91652be5SDavid Howells fn(crypto_cipher_tfm(tfm), src, iv); 91*91652be5SDavid Howells memcpy(iv, src, bsize); 92*91652be5SDavid Howells xor(iv, tmpbuf, bsize); 93*91652be5SDavid Howells 94*91652be5SDavid Howells src += bsize; 95*91652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 96*91652be5SDavid Howells 97*91652be5SDavid Howells memcpy(walk->iv, iv, bsize); 98*91652be5SDavid Howells 99*91652be5SDavid Howells return nbytes; 100*91652be5SDavid Howells } 101*91652be5SDavid Howells 102*91652be5SDavid Howells static int crypto_pcbc_encrypt(struct blkcipher_desc *desc, 103*91652be5SDavid Howells struct scatterlist *dst, struct scatterlist *src, 104*91652be5SDavid Howells unsigned int nbytes) 105*91652be5SDavid Howells { 106*91652be5SDavid Howells struct blkcipher_walk walk; 107*91652be5SDavid Howells struct crypto_blkcipher *tfm = desc->tfm; 108*91652be5SDavid Howells struct crypto_pcbc_ctx *ctx = crypto_blkcipher_ctx(tfm); 109*91652be5SDavid Howells struct crypto_cipher *child = ctx->child; 110*91652be5SDavid Howells void (*xor)(u8 *, const u8 *, unsigned int bs) = ctx->xor; 111*91652be5SDavid Howells int err; 112*91652be5SDavid Howells 113*91652be5SDavid Howells blkcipher_walk_init(&walk, dst, src, nbytes); 114*91652be5SDavid Howells err = blkcipher_walk_virt(desc, &walk); 115*91652be5SDavid Howells 116*91652be5SDavid Howells while ((nbytes = walk.nbytes)) { 117*91652be5SDavid Howells if (walk.src.virt.addr == walk.dst.virt.addr) 118*91652be5SDavid Howells nbytes = crypto_pcbc_encrypt_inplace(desc, &walk, child, 119*91652be5SDavid Howells xor); 120*91652be5SDavid Howells else 121*91652be5SDavid Howells nbytes = crypto_pcbc_encrypt_segment(desc, &walk, child, 122*91652be5SDavid Howells xor); 123*91652be5SDavid Howells err = blkcipher_walk_done(desc, &walk, nbytes); 124*91652be5SDavid Howells } 125*91652be5SDavid Howells 126*91652be5SDavid Howells return err; 127*91652be5SDavid Howells } 128*91652be5SDavid Howells 129*91652be5SDavid Howells static int crypto_pcbc_decrypt_segment(struct blkcipher_desc *desc, 130*91652be5SDavid Howells struct blkcipher_walk *walk, 131*91652be5SDavid Howells struct crypto_cipher *tfm, 132*91652be5SDavid Howells void (*xor)(u8 *, const u8 *, 133*91652be5SDavid Howells unsigned int)) 134*91652be5SDavid Howells { 135*91652be5SDavid Howells void (*fn)(struct crypto_tfm *, u8 *, const u8 *) = 136*91652be5SDavid Howells crypto_cipher_alg(tfm)->cia_decrypt; 137*91652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 138*91652be5SDavid Howells unsigned int nbytes = walk->nbytes; 139*91652be5SDavid Howells u8 *src = walk->src.virt.addr; 140*91652be5SDavid Howells u8 *dst = walk->dst.virt.addr; 141*91652be5SDavid Howells u8 *iv = walk->iv; 142*91652be5SDavid Howells 143*91652be5SDavid Howells do { 144*91652be5SDavid Howells fn(crypto_cipher_tfm(tfm), dst, src); 145*91652be5SDavid Howells xor(dst, iv, bsize); 146*91652be5SDavid Howells memcpy(iv, src, bsize); 147*91652be5SDavid Howells xor(iv, dst, bsize); 148*91652be5SDavid Howells 149*91652be5SDavid Howells src += bsize; 150*91652be5SDavid Howells dst += bsize; 151*91652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 152*91652be5SDavid Howells 153*91652be5SDavid Howells memcpy(walk->iv, iv, bsize); 154*91652be5SDavid Howells 155*91652be5SDavid Howells return nbytes; 156*91652be5SDavid Howells } 157*91652be5SDavid Howells 158*91652be5SDavid Howells static int crypto_pcbc_decrypt_inplace(struct blkcipher_desc *desc, 159*91652be5SDavid Howells struct blkcipher_walk *walk, 160*91652be5SDavid Howells struct crypto_cipher *tfm, 161*91652be5SDavid Howells void (*xor)(u8 *, const u8 *, 162*91652be5SDavid Howells unsigned int)) 163*91652be5SDavid Howells { 164*91652be5SDavid Howells void (*fn)(struct crypto_tfm *, u8 *, const u8 *) = 165*91652be5SDavid Howells crypto_cipher_alg(tfm)->cia_decrypt; 166*91652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 167*91652be5SDavid Howells unsigned int nbytes = walk->nbytes; 168*91652be5SDavid Howells u8 *src = walk->src.virt.addr; 169*91652be5SDavid Howells u8 *iv = walk->iv; 170*91652be5SDavid Howells u8 tmpbuf[bsize]; 171*91652be5SDavid Howells 172*91652be5SDavid Howells do { 173*91652be5SDavid Howells memcpy(tmpbuf, src, bsize); 174*91652be5SDavid Howells fn(crypto_cipher_tfm(tfm), src, src); 175*91652be5SDavid Howells xor(src, iv, bsize); 176*91652be5SDavid Howells memcpy(iv, tmpbuf, bsize); 177*91652be5SDavid Howells xor(iv, src, bsize); 178*91652be5SDavid Howells 179*91652be5SDavid Howells src += bsize; 180*91652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 181*91652be5SDavid Howells 182*91652be5SDavid Howells memcpy(walk->iv, iv, bsize); 183*91652be5SDavid Howells 184*91652be5SDavid Howells return nbytes; 185*91652be5SDavid Howells } 186*91652be5SDavid Howells 187*91652be5SDavid Howells static int crypto_pcbc_decrypt(struct blkcipher_desc *desc, 188*91652be5SDavid Howells struct scatterlist *dst, struct scatterlist *src, 189*91652be5SDavid Howells unsigned int nbytes) 190*91652be5SDavid Howells { 191*91652be5SDavid Howells struct blkcipher_walk walk; 192*91652be5SDavid Howells struct crypto_blkcipher *tfm = desc->tfm; 193*91652be5SDavid Howells struct crypto_pcbc_ctx *ctx = crypto_blkcipher_ctx(tfm); 194*91652be5SDavid Howells struct crypto_cipher *child = ctx->child; 195*91652be5SDavid Howells void (*xor)(u8 *, const u8 *, unsigned int bs) = ctx->xor; 196*91652be5SDavid Howells int err; 197*91652be5SDavid Howells 198*91652be5SDavid Howells blkcipher_walk_init(&walk, dst, src, nbytes); 199*91652be5SDavid Howells err = blkcipher_walk_virt(desc, &walk); 200*91652be5SDavid Howells 201*91652be5SDavid Howells while ((nbytes = walk.nbytes)) { 202*91652be5SDavid Howells if (walk.src.virt.addr == walk.dst.virt.addr) 203*91652be5SDavid Howells nbytes = crypto_pcbc_decrypt_inplace(desc, &walk, child, 204*91652be5SDavid Howells xor); 205*91652be5SDavid Howells else 206*91652be5SDavid Howells nbytes = crypto_pcbc_decrypt_segment(desc, &walk, child, 207*91652be5SDavid Howells xor); 208*91652be5SDavid Howells err = blkcipher_walk_done(desc, &walk, nbytes); 209*91652be5SDavid Howells } 210*91652be5SDavid Howells 211*91652be5SDavid Howells return err; 212*91652be5SDavid Howells } 213*91652be5SDavid Howells 214*91652be5SDavid Howells static void xor_byte(u8 *a, const u8 *b, unsigned int bs) 215*91652be5SDavid Howells { 216*91652be5SDavid Howells do { 217*91652be5SDavid Howells *a++ ^= *b++; 218*91652be5SDavid Howells } while (--bs); 219*91652be5SDavid Howells } 220*91652be5SDavid Howells 221*91652be5SDavid Howells static void xor_quad(u8 *dst, const u8 *src, unsigned int bs) 222*91652be5SDavid Howells { 223*91652be5SDavid Howells u32 *a = (u32 *)dst; 224*91652be5SDavid Howells u32 *b = (u32 *)src; 225*91652be5SDavid Howells 226*91652be5SDavid Howells do { 227*91652be5SDavid Howells *a++ ^= *b++; 228*91652be5SDavid Howells } while ((bs -= 4)); 229*91652be5SDavid Howells } 230*91652be5SDavid Howells 231*91652be5SDavid Howells static void xor_64(u8 *a, const u8 *b, unsigned int bs) 232*91652be5SDavid Howells { 233*91652be5SDavid Howells ((u32 *)a)[0] ^= ((u32 *)b)[0]; 234*91652be5SDavid Howells ((u32 *)a)[1] ^= ((u32 *)b)[1]; 235*91652be5SDavid Howells } 236*91652be5SDavid Howells 237*91652be5SDavid Howells static void xor_128(u8 *a, const u8 *b, unsigned int bs) 238*91652be5SDavid Howells { 239*91652be5SDavid Howells ((u32 *)a)[0] ^= ((u32 *)b)[0]; 240*91652be5SDavid Howells ((u32 *)a)[1] ^= ((u32 *)b)[1]; 241*91652be5SDavid Howells ((u32 *)a)[2] ^= ((u32 *)b)[2]; 242*91652be5SDavid Howells ((u32 *)a)[3] ^= ((u32 *)b)[3]; 243*91652be5SDavid Howells } 244*91652be5SDavid Howells 245*91652be5SDavid Howells static int crypto_pcbc_init_tfm(struct crypto_tfm *tfm) 246*91652be5SDavid Howells { 247*91652be5SDavid Howells struct crypto_instance *inst = (void *)tfm->__crt_alg; 248*91652be5SDavid Howells struct crypto_spawn *spawn = crypto_instance_ctx(inst); 249*91652be5SDavid Howells struct crypto_pcbc_ctx *ctx = crypto_tfm_ctx(tfm); 250*91652be5SDavid Howells 251*91652be5SDavid Howells switch (crypto_tfm_alg_blocksize(tfm)) { 252*91652be5SDavid Howells case 8: 253*91652be5SDavid Howells ctx->xor = xor_64; 254*91652be5SDavid Howells break; 255*91652be5SDavid Howells 256*91652be5SDavid Howells case 16: 257*91652be5SDavid Howells ctx->xor = xor_128; 258*91652be5SDavid Howells break; 259*91652be5SDavid Howells 260*91652be5SDavid Howells default: 261*91652be5SDavid Howells if (crypto_tfm_alg_blocksize(tfm) % 4) 262*91652be5SDavid Howells ctx->xor = xor_byte; 263*91652be5SDavid Howells else 264*91652be5SDavid Howells ctx->xor = xor_quad; 265*91652be5SDavid Howells } 266*91652be5SDavid Howells 267*91652be5SDavid Howells tfm = crypto_spawn_tfm(spawn); 268*91652be5SDavid Howells if (IS_ERR(tfm)) 269*91652be5SDavid Howells return PTR_ERR(tfm); 270*91652be5SDavid Howells 271*91652be5SDavid Howells ctx->child = crypto_cipher_cast(tfm); 272*91652be5SDavid Howells return 0; 273*91652be5SDavid Howells } 274*91652be5SDavid Howells 275*91652be5SDavid Howells static void crypto_pcbc_exit_tfm(struct crypto_tfm *tfm) 276*91652be5SDavid Howells { 277*91652be5SDavid Howells struct crypto_pcbc_ctx *ctx = crypto_tfm_ctx(tfm); 278*91652be5SDavid Howells crypto_free_cipher(ctx->child); 279*91652be5SDavid Howells } 280*91652be5SDavid Howells 281*91652be5SDavid Howells static struct crypto_instance *crypto_pcbc_alloc(void *param, unsigned int len) 282*91652be5SDavid Howells { 283*91652be5SDavid Howells struct crypto_instance *inst; 284*91652be5SDavid Howells struct crypto_alg *alg; 285*91652be5SDavid Howells 286*91652be5SDavid Howells alg = crypto_get_attr_alg(param, len, CRYPTO_ALG_TYPE_CIPHER, 287*91652be5SDavid Howells CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_ASYNC); 288*91652be5SDavid Howells if (IS_ERR(alg)) 289*91652be5SDavid Howells return ERR_PTR(PTR_ERR(alg)); 290*91652be5SDavid Howells 291*91652be5SDavid Howells inst = crypto_alloc_instance("pcbc", alg); 292*91652be5SDavid Howells if (IS_ERR(inst)) 293*91652be5SDavid Howells goto out_put_alg; 294*91652be5SDavid Howells 295*91652be5SDavid Howells inst->alg.cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER; 296*91652be5SDavid Howells inst->alg.cra_priority = alg->cra_priority; 297*91652be5SDavid Howells inst->alg.cra_blocksize = alg->cra_blocksize; 298*91652be5SDavid Howells inst->alg.cra_alignmask = alg->cra_alignmask; 299*91652be5SDavid Howells inst->alg.cra_type = &crypto_blkcipher_type; 300*91652be5SDavid Howells 301*91652be5SDavid Howells if (!(alg->cra_blocksize % 4)) 302*91652be5SDavid Howells inst->alg.cra_alignmask |= 3; 303*91652be5SDavid Howells inst->alg.cra_blkcipher.ivsize = alg->cra_blocksize; 304*91652be5SDavid Howells inst->alg.cra_blkcipher.min_keysize = alg->cra_cipher.cia_min_keysize; 305*91652be5SDavid Howells inst->alg.cra_blkcipher.max_keysize = alg->cra_cipher.cia_max_keysize; 306*91652be5SDavid Howells 307*91652be5SDavid Howells inst->alg.cra_ctxsize = sizeof(struct crypto_pcbc_ctx); 308*91652be5SDavid Howells 309*91652be5SDavid Howells inst->alg.cra_init = crypto_pcbc_init_tfm; 310*91652be5SDavid Howells inst->alg.cra_exit = crypto_pcbc_exit_tfm; 311*91652be5SDavid Howells 312*91652be5SDavid Howells inst->alg.cra_blkcipher.setkey = crypto_pcbc_setkey; 313*91652be5SDavid Howells inst->alg.cra_blkcipher.encrypt = crypto_pcbc_encrypt; 314*91652be5SDavid Howells inst->alg.cra_blkcipher.decrypt = crypto_pcbc_decrypt; 315*91652be5SDavid Howells 316*91652be5SDavid Howells out_put_alg: 317*91652be5SDavid Howells crypto_mod_put(alg); 318*91652be5SDavid Howells return inst; 319*91652be5SDavid Howells } 320*91652be5SDavid Howells 321*91652be5SDavid Howells static void crypto_pcbc_free(struct crypto_instance *inst) 322*91652be5SDavid Howells { 323*91652be5SDavid Howells crypto_drop_spawn(crypto_instance_ctx(inst)); 324*91652be5SDavid Howells kfree(inst); 325*91652be5SDavid Howells } 326*91652be5SDavid Howells 327*91652be5SDavid Howells static struct crypto_template crypto_pcbc_tmpl = { 328*91652be5SDavid Howells .name = "pcbc", 329*91652be5SDavid Howells .alloc = crypto_pcbc_alloc, 330*91652be5SDavid Howells .free = crypto_pcbc_free, 331*91652be5SDavid Howells .module = THIS_MODULE, 332*91652be5SDavid Howells }; 333*91652be5SDavid Howells 334*91652be5SDavid Howells static int __init crypto_pcbc_module_init(void) 335*91652be5SDavid Howells { 336*91652be5SDavid Howells return crypto_register_template(&crypto_pcbc_tmpl); 337*91652be5SDavid Howells } 338*91652be5SDavid Howells 339*91652be5SDavid Howells static void __exit crypto_pcbc_module_exit(void) 340*91652be5SDavid Howells { 341*91652be5SDavid Howells crypto_unregister_template(&crypto_pcbc_tmpl); 342*91652be5SDavid Howells } 343*91652be5SDavid Howells 344*91652be5SDavid Howells module_init(crypto_pcbc_module_init); 345*91652be5SDavid Howells module_exit(crypto_pcbc_module_exit); 346*91652be5SDavid Howells 347*91652be5SDavid Howells MODULE_LICENSE("GPL"); 348*91652be5SDavid Howells MODULE_DESCRIPTION("PCBC block cipher algorithm"); 349