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 1791652be5SDavid Howells #include <crypto/algapi.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/scatterlist.h> 2391652be5SDavid Howells #include <linux/slab.h> 2491652be5SDavid Howells 2591652be5SDavid Howells struct crypto_pcbc_ctx { 2691652be5SDavid Howells struct crypto_cipher *child; 2791652be5SDavid Howells void (*xor)(u8 *dst, const u8 *src, unsigned int bs); 2891652be5SDavid Howells }; 2991652be5SDavid Howells 3091652be5SDavid Howells static int crypto_pcbc_setkey(struct crypto_tfm *parent, const u8 *key, 3191652be5SDavid Howells unsigned int keylen) 3291652be5SDavid Howells { 3391652be5SDavid Howells struct crypto_pcbc_ctx *ctx = crypto_tfm_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); 3891652be5SDavid Howells crypto_cipher_set_flags(child, crypto_tfm_get_flags(parent) & 3991652be5SDavid Howells CRYPTO_TFM_REQ_MASK); 4091652be5SDavid Howells err = crypto_cipher_setkey(child, key, keylen); 4191652be5SDavid Howells crypto_tfm_set_flags(parent, crypto_cipher_get_flags(child) & 4291652be5SDavid Howells CRYPTO_TFM_RES_MASK); 4391652be5SDavid Howells return err; 4491652be5SDavid Howells } 4591652be5SDavid Howells 4691652be5SDavid Howells static int crypto_pcbc_encrypt_segment(struct blkcipher_desc *desc, 4791652be5SDavid Howells struct blkcipher_walk *walk, 4891652be5SDavid Howells struct crypto_cipher *tfm, 4991652be5SDavid Howells void (*xor)(u8 *, const u8 *, 5091652be5SDavid Howells unsigned int)) 5191652be5SDavid Howells { 5291652be5SDavid Howells void (*fn)(struct crypto_tfm *, u8 *, const u8 *) = 5391652be5SDavid Howells crypto_cipher_alg(tfm)->cia_encrypt; 5491652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 5591652be5SDavid Howells unsigned int nbytes = walk->nbytes; 5691652be5SDavid Howells u8 *src = walk->src.virt.addr; 5791652be5SDavid Howells u8 *dst = walk->dst.virt.addr; 5891652be5SDavid Howells u8 *iv = walk->iv; 5991652be5SDavid Howells 6091652be5SDavid Howells do { 6191652be5SDavid Howells xor(iv, src, bsize); 6291652be5SDavid Howells fn(crypto_cipher_tfm(tfm), dst, iv); 6391652be5SDavid Howells memcpy(iv, dst, bsize); 6491652be5SDavid Howells xor(iv, src, bsize); 6591652be5SDavid Howells 6691652be5SDavid Howells src += bsize; 6791652be5SDavid Howells dst += bsize; 6891652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 6991652be5SDavid Howells 7091652be5SDavid Howells return nbytes; 7191652be5SDavid Howells } 7291652be5SDavid Howells 7391652be5SDavid Howells static int crypto_pcbc_encrypt_inplace(struct blkcipher_desc *desc, 7491652be5SDavid Howells struct blkcipher_walk *walk, 7591652be5SDavid Howells struct crypto_cipher *tfm, 7691652be5SDavid Howells void (*xor)(u8 *, const u8 *, 7791652be5SDavid Howells unsigned int)) 7891652be5SDavid Howells { 7991652be5SDavid Howells void (*fn)(struct crypto_tfm *, u8 *, const u8 *) = 8091652be5SDavid Howells crypto_cipher_alg(tfm)->cia_encrypt; 8191652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 8291652be5SDavid Howells unsigned int nbytes = walk->nbytes; 8391652be5SDavid Howells u8 *src = walk->src.virt.addr; 8491652be5SDavid Howells u8 *iv = walk->iv; 8591652be5SDavid Howells u8 tmpbuf[bsize]; 8691652be5SDavid Howells 8791652be5SDavid Howells do { 8891652be5SDavid Howells memcpy(tmpbuf, src, bsize); 8991652be5SDavid Howells xor(iv, tmpbuf, bsize); 9091652be5SDavid Howells fn(crypto_cipher_tfm(tfm), src, iv); 9191652be5SDavid Howells memcpy(iv, src, bsize); 9291652be5SDavid Howells xor(iv, tmpbuf, bsize); 9391652be5SDavid Howells 9491652be5SDavid Howells src += bsize; 9591652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 9691652be5SDavid Howells 9791652be5SDavid Howells memcpy(walk->iv, iv, bsize); 9891652be5SDavid Howells 9991652be5SDavid Howells return nbytes; 10091652be5SDavid Howells } 10191652be5SDavid Howells 10291652be5SDavid Howells static int crypto_pcbc_encrypt(struct blkcipher_desc *desc, 10391652be5SDavid Howells struct scatterlist *dst, struct scatterlist *src, 10491652be5SDavid Howells unsigned int nbytes) 10591652be5SDavid Howells { 10691652be5SDavid Howells struct blkcipher_walk walk; 10791652be5SDavid Howells struct crypto_blkcipher *tfm = desc->tfm; 10891652be5SDavid Howells struct crypto_pcbc_ctx *ctx = crypto_blkcipher_ctx(tfm); 10991652be5SDavid Howells struct crypto_cipher *child = ctx->child; 11091652be5SDavid Howells void (*xor)(u8 *, const u8 *, unsigned int bs) = ctx->xor; 11191652be5SDavid Howells int err; 11291652be5SDavid Howells 11391652be5SDavid Howells blkcipher_walk_init(&walk, dst, src, nbytes); 11491652be5SDavid Howells err = blkcipher_walk_virt(desc, &walk); 11591652be5SDavid Howells 11691652be5SDavid Howells while ((nbytes = walk.nbytes)) { 11791652be5SDavid Howells if (walk.src.virt.addr == walk.dst.virt.addr) 11891652be5SDavid Howells nbytes = crypto_pcbc_encrypt_inplace(desc, &walk, child, 11991652be5SDavid Howells xor); 12091652be5SDavid Howells else 12191652be5SDavid Howells nbytes = crypto_pcbc_encrypt_segment(desc, &walk, child, 12291652be5SDavid Howells xor); 12391652be5SDavid Howells err = blkcipher_walk_done(desc, &walk, nbytes); 12491652be5SDavid Howells } 12591652be5SDavid Howells 12691652be5SDavid Howells return err; 12791652be5SDavid Howells } 12891652be5SDavid Howells 12991652be5SDavid Howells static int crypto_pcbc_decrypt_segment(struct blkcipher_desc *desc, 13091652be5SDavid Howells struct blkcipher_walk *walk, 13191652be5SDavid Howells struct crypto_cipher *tfm, 13291652be5SDavid Howells void (*xor)(u8 *, const u8 *, 13391652be5SDavid Howells unsigned int)) 13491652be5SDavid Howells { 13591652be5SDavid Howells void (*fn)(struct crypto_tfm *, u8 *, const u8 *) = 13691652be5SDavid Howells crypto_cipher_alg(tfm)->cia_decrypt; 13791652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 13891652be5SDavid Howells unsigned int nbytes = walk->nbytes; 13991652be5SDavid Howells u8 *src = walk->src.virt.addr; 14091652be5SDavid Howells u8 *dst = walk->dst.virt.addr; 14191652be5SDavid Howells u8 *iv = walk->iv; 14291652be5SDavid Howells 14391652be5SDavid Howells do { 14491652be5SDavid Howells fn(crypto_cipher_tfm(tfm), dst, src); 14591652be5SDavid Howells xor(dst, iv, bsize); 14691652be5SDavid Howells memcpy(iv, src, bsize); 14791652be5SDavid Howells xor(iv, dst, bsize); 14891652be5SDavid Howells 14991652be5SDavid Howells src += bsize; 15091652be5SDavid Howells dst += bsize; 15191652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 15291652be5SDavid Howells 15391652be5SDavid Howells memcpy(walk->iv, iv, bsize); 15491652be5SDavid Howells 15591652be5SDavid Howells return nbytes; 15691652be5SDavid Howells } 15791652be5SDavid Howells 15891652be5SDavid Howells static int crypto_pcbc_decrypt_inplace(struct blkcipher_desc *desc, 15991652be5SDavid Howells struct blkcipher_walk *walk, 16091652be5SDavid Howells struct crypto_cipher *tfm, 16191652be5SDavid Howells void (*xor)(u8 *, const u8 *, 16291652be5SDavid Howells unsigned int)) 16391652be5SDavid Howells { 16491652be5SDavid Howells void (*fn)(struct crypto_tfm *, u8 *, const u8 *) = 16591652be5SDavid Howells crypto_cipher_alg(tfm)->cia_decrypt; 16691652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm); 16791652be5SDavid Howells unsigned int nbytes = walk->nbytes; 16891652be5SDavid Howells u8 *src = walk->src.virt.addr; 16991652be5SDavid Howells u8 *iv = walk->iv; 17091652be5SDavid Howells u8 tmpbuf[bsize]; 17191652be5SDavid Howells 17291652be5SDavid Howells do { 17391652be5SDavid Howells memcpy(tmpbuf, src, bsize); 17491652be5SDavid Howells fn(crypto_cipher_tfm(tfm), src, src); 17591652be5SDavid Howells xor(src, iv, bsize); 17691652be5SDavid Howells memcpy(iv, tmpbuf, bsize); 17791652be5SDavid Howells xor(iv, src, bsize); 17891652be5SDavid Howells 17991652be5SDavid Howells src += bsize; 18091652be5SDavid Howells } while ((nbytes -= bsize) >= bsize); 18191652be5SDavid Howells 18291652be5SDavid Howells memcpy(walk->iv, iv, bsize); 18391652be5SDavid Howells 18491652be5SDavid Howells return nbytes; 18591652be5SDavid Howells } 18691652be5SDavid Howells 18791652be5SDavid Howells static int crypto_pcbc_decrypt(struct blkcipher_desc *desc, 18891652be5SDavid Howells struct scatterlist *dst, struct scatterlist *src, 18991652be5SDavid Howells unsigned int nbytes) 19091652be5SDavid Howells { 19191652be5SDavid Howells struct blkcipher_walk walk; 19291652be5SDavid Howells struct crypto_blkcipher *tfm = desc->tfm; 19391652be5SDavid Howells struct crypto_pcbc_ctx *ctx = crypto_blkcipher_ctx(tfm); 19491652be5SDavid Howells struct crypto_cipher *child = ctx->child; 19591652be5SDavid Howells void (*xor)(u8 *, const u8 *, unsigned int bs) = ctx->xor; 19691652be5SDavid Howells int err; 19791652be5SDavid Howells 19891652be5SDavid Howells blkcipher_walk_init(&walk, dst, src, nbytes); 19991652be5SDavid Howells err = blkcipher_walk_virt(desc, &walk); 20091652be5SDavid Howells 20191652be5SDavid Howells while ((nbytes = walk.nbytes)) { 20291652be5SDavid Howells if (walk.src.virt.addr == walk.dst.virt.addr) 20391652be5SDavid Howells nbytes = crypto_pcbc_decrypt_inplace(desc, &walk, child, 20491652be5SDavid Howells xor); 20591652be5SDavid Howells else 20691652be5SDavid Howells nbytes = crypto_pcbc_decrypt_segment(desc, &walk, child, 20791652be5SDavid Howells xor); 20891652be5SDavid Howells err = blkcipher_walk_done(desc, &walk, nbytes); 20991652be5SDavid Howells } 21091652be5SDavid Howells 21191652be5SDavid Howells return err; 21291652be5SDavid Howells } 21391652be5SDavid Howells 21491652be5SDavid Howells static void xor_byte(u8 *a, const u8 *b, unsigned int bs) 21591652be5SDavid Howells { 21691652be5SDavid Howells do { 21791652be5SDavid Howells *a++ ^= *b++; 21891652be5SDavid Howells } while (--bs); 21991652be5SDavid Howells } 22091652be5SDavid Howells 22191652be5SDavid Howells static void xor_quad(u8 *dst, const u8 *src, unsigned int bs) 22291652be5SDavid Howells { 22391652be5SDavid Howells u32 *a = (u32 *)dst; 22491652be5SDavid Howells u32 *b = (u32 *)src; 22591652be5SDavid Howells 22691652be5SDavid Howells do { 22791652be5SDavid Howells *a++ ^= *b++; 22891652be5SDavid Howells } while ((bs -= 4)); 22991652be5SDavid Howells } 23091652be5SDavid Howells 23191652be5SDavid Howells static void xor_64(u8 *a, const u8 *b, unsigned int bs) 23291652be5SDavid Howells { 23391652be5SDavid Howells ((u32 *)a)[0] ^= ((u32 *)b)[0]; 23491652be5SDavid Howells ((u32 *)a)[1] ^= ((u32 *)b)[1]; 23591652be5SDavid Howells } 23691652be5SDavid Howells 23791652be5SDavid Howells static void xor_128(u8 *a, const u8 *b, unsigned int bs) 23891652be5SDavid Howells { 23991652be5SDavid Howells ((u32 *)a)[0] ^= ((u32 *)b)[0]; 24091652be5SDavid Howells ((u32 *)a)[1] ^= ((u32 *)b)[1]; 24191652be5SDavid Howells ((u32 *)a)[2] ^= ((u32 *)b)[2]; 24291652be5SDavid Howells ((u32 *)a)[3] ^= ((u32 *)b)[3]; 24391652be5SDavid Howells } 24491652be5SDavid Howells 24591652be5SDavid Howells static int crypto_pcbc_init_tfm(struct crypto_tfm *tfm) 24691652be5SDavid Howells { 24791652be5SDavid Howells struct crypto_instance *inst = (void *)tfm->__crt_alg; 24891652be5SDavid Howells struct crypto_spawn *spawn = crypto_instance_ctx(inst); 24991652be5SDavid Howells struct crypto_pcbc_ctx *ctx = crypto_tfm_ctx(tfm); 2502e306ee0SHerbert Xu struct crypto_cipher *cipher; 25191652be5SDavid Howells 25291652be5SDavid Howells switch (crypto_tfm_alg_blocksize(tfm)) { 25391652be5SDavid Howells case 8: 25491652be5SDavid Howells ctx->xor = xor_64; 25591652be5SDavid Howells break; 25691652be5SDavid Howells 25791652be5SDavid Howells case 16: 25891652be5SDavid Howells ctx->xor = xor_128; 25991652be5SDavid Howells break; 26091652be5SDavid Howells 26191652be5SDavid Howells default: 26291652be5SDavid Howells if (crypto_tfm_alg_blocksize(tfm) % 4) 26391652be5SDavid Howells ctx->xor = xor_byte; 26491652be5SDavid Howells else 26591652be5SDavid Howells ctx->xor = xor_quad; 26691652be5SDavid Howells } 26791652be5SDavid Howells 2682e306ee0SHerbert Xu cipher = crypto_spawn_cipher(spawn); 2692e306ee0SHerbert Xu if (IS_ERR(cipher)) 2702e306ee0SHerbert Xu return PTR_ERR(cipher); 27191652be5SDavid Howells 2722e306ee0SHerbert Xu ctx->child = cipher; 27391652be5SDavid Howells return 0; 27491652be5SDavid Howells } 27591652be5SDavid Howells 27691652be5SDavid Howells static void crypto_pcbc_exit_tfm(struct crypto_tfm *tfm) 27791652be5SDavid Howells { 27891652be5SDavid Howells struct crypto_pcbc_ctx *ctx = crypto_tfm_ctx(tfm); 27991652be5SDavid Howells crypto_free_cipher(ctx->child); 28091652be5SDavid Howells } 28191652be5SDavid Howells 282*ebc610e5SHerbert Xu static struct crypto_instance *crypto_pcbc_alloc(struct rtattr **tb) 28391652be5SDavid Howells { 28491652be5SDavid Howells struct crypto_instance *inst; 28591652be5SDavid Howells struct crypto_alg *alg; 286*ebc610e5SHerbert Xu int err; 28791652be5SDavid Howells 288*ebc610e5SHerbert Xu err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_BLKCIPHER); 289*ebc610e5SHerbert Xu if (err) 290*ebc610e5SHerbert Xu return ERR_PTR(err); 291*ebc610e5SHerbert Xu 292*ebc610e5SHerbert Xu alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER, 293*ebc610e5SHerbert Xu CRYPTO_ALG_TYPE_MASK); 29491652be5SDavid Howells if (IS_ERR(alg)) 29591652be5SDavid Howells return ERR_PTR(PTR_ERR(alg)); 29691652be5SDavid Howells 29791652be5SDavid Howells inst = crypto_alloc_instance("pcbc", alg); 29891652be5SDavid Howells if (IS_ERR(inst)) 29991652be5SDavid Howells goto out_put_alg; 30091652be5SDavid Howells 30191652be5SDavid Howells inst->alg.cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER; 30291652be5SDavid Howells inst->alg.cra_priority = alg->cra_priority; 30391652be5SDavid Howells inst->alg.cra_blocksize = alg->cra_blocksize; 30491652be5SDavid Howells inst->alg.cra_alignmask = alg->cra_alignmask; 30591652be5SDavid Howells inst->alg.cra_type = &crypto_blkcipher_type; 30691652be5SDavid Howells 30791652be5SDavid Howells if (!(alg->cra_blocksize % 4)) 30891652be5SDavid Howells inst->alg.cra_alignmask |= 3; 30991652be5SDavid Howells inst->alg.cra_blkcipher.ivsize = alg->cra_blocksize; 31091652be5SDavid Howells inst->alg.cra_blkcipher.min_keysize = alg->cra_cipher.cia_min_keysize; 31191652be5SDavid Howells inst->alg.cra_blkcipher.max_keysize = alg->cra_cipher.cia_max_keysize; 31291652be5SDavid Howells 31391652be5SDavid Howells inst->alg.cra_ctxsize = sizeof(struct crypto_pcbc_ctx); 31491652be5SDavid Howells 31591652be5SDavid Howells inst->alg.cra_init = crypto_pcbc_init_tfm; 31691652be5SDavid Howells inst->alg.cra_exit = crypto_pcbc_exit_tfm; 31791652be5SDavid Howells 31891652be5SDavid Howells inst->alg.cra_blkcipher.setkey = crypto_pcbc_setkey; 31991652be5SDavid Howells inst->alg.cra_blkcipher.encrypt = crypto_pcbc_encrypt; 32091652be5SDavid Howells inst->alg.cra_blkcipher.decrypt = crypto_pcbc_decrypt; 32191652be5SDavid Howells 32291652be5SDavid Howells out_put_alg: 32391652be5SDavid Howells crypto_mod_put(alg); 32491652be5SDavid Howells return inst; 32591652be5SDavid Howells } 32691652be5SDavid Howells 32791652be5SDavid Howells static void crypto_pcbc_free(struct crypto_instance *inst) 32891652be5SDavid Howells { 32991652be5SDavid Howells crypto_drop_spawn(crypto_instance_ctx(inst)); 33091652be5SDavid Howells kfree(inst); 33191652be5SDavid Howells } 33291652be5SDavid Howells 33391652be5SDavid Howells static struct crypto_template crypto_pcbc_tmpl = { 33491652be5SDavid Howells .name = "pcbc", 33591652be5SDavid Howells .alloc = crypto_pcbc_alloc, 33691652be5SDavid Howells .free = crypto_pcbc_free, 33791652be5SDavid Howells .module = THIS_MODULE, 33891652be5SDavid Howells }; 33991652be5SDavid Howells 34091652be5SDavid Howells static int __init crypto_pcbc_module_init(void) 34191652be5SDavid Howells { 34291652be5SDavid Howells return crypto_register_template(&crypto_pcbc_tmpl); 34391652be5SDavid Howells } 34491652be5SDavid Howells 34591652be5SDavid Howells static void __exit crypto_pcbc_module_exit(void) 34691652be5SDavid Howells { 34791652be5SDavid Howells crypto_unregister_template(&crypto_pcbc_tmpl); 34891652be5SDavid Howells } 34991652be5SDavid Howells 35091652be5SDavid Howells module_init(crypto_pcbc_module_init); 35191652be5SDavid Howells module_exit(crypto_pcbc_module_exit); 35291652be5SDavid Howells 35391652be5SDavid Howells MODULE_LICENSE("GPL"); 35491652be5SDavid Howells MODULE_DESCRIPTION("PCBC block cipher algorithm"); 355