linux/crypto/pcbc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * PCBC: Propagating Cipher Block Chaining mode
   4 *
   5 * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
   6 * Written by David Howells (dhowells@redhat.com)
   7 *
   8 * Derived from cbc.c
   9 * - Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au>
  10 */
  11
  12#include <crypto/algapi.h>
  13#include <crypto/internal/skcipher.h>
  14#include <linux/err.h>
  15#include <linux/init.h>
  16#include <linux/kernel.h>
  17#include <linux/module.h>
  18
  19static int crypto_pcbc_encrypt_segment(struct skcipher_request *req,
  20                                       struct skcipher_walk *walk,
  21                                       struct crypto_cipher *tfm)
  22{
  23        int bsize = crypto_cipher_blocksize(tfm);
  24        unsigned int nbytes = walk->nbytes;
  25        u8 *src = walk->src.virt.addr;
  26        u8 *dst = walk->dst.virt.addr;
  27        u8 * const iv = walk->iv;
  28
  29        do {
  30                crypto_xor(iv, src, bsize);
  31                crypto_cipher_encrypt_one(tfm, dst, iv);
  32                crypto_xor_cpy(iv, dst, src, bsize);
  33
  34                src += bsize;
  35                dst += bsize;
  36        } while ((nbytes -= bsize) >= bsize);
  37
  38        return nbytes;
  39}
  40
  41static int crypto_pcbc_encrypt_inplace(struct skcipher_request *req,
  42                                       struct skcipher_walk *walk,
  43                                       struct crypto_cipher *tfm)
  44{
  45        int bsize = crypto_cipher_blocksize(tfm);
  46        unsigned int nbytes = walk->nbytes;
  47        u8 *src = walk->src.virt.addr;
  48        u8 * const iv = walk->iv;
  49        u8 tmpbuf[MAX_CIPHER_BLOCKSIZE];
  50
  51        do {
  52                memcpy(tmpbuf, src, bsize);
  53                crypto_xor(iv, src, bsize);
  54                crypto_cipher_encrypt_one(tfm, src, iv);
  55                crypto_xor_cpy(iv, tmpbuf, src, bsize);
  56
  57                src += bsize;
  58        } while ((nbytes -= bsize) >= bsize);
  59
  60        return nbytes;
  61}
  62
  63static int crypto_pcbc_encrypt(struct skcipher_request *req)
  64{
  65        struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
  66        struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
  67        struct skcipher_walk walk;
  68        unsigned int nbytes;
  69        int err;
  70
  71        err = skcipher_walk_virt(&walk, req, false);
  72
  73        while ((nbytes = walk.nbytes)) {
  74                if (walk.src.virt.addr == walk.dst.virt.addr)
  75                        nbytes = crypto_pcbc_encrypt_inplace(req, &walk,
  76                                                             cipher);
  77                else
  78                        nbytes = crypto_pcbc_encrypt_segment(req, &walk,
  79                                                             cipher);
  80                err = skcipher_walk_done(&walk, nbytes);
  81        }
  82
  83        return err;
  84}
  85
  86static int crypto_pcbc_decrypt_segment(struct skcipher_request *req,
  87                                       struct skcipher_walk *walk,
  88                                       struct crypto_cipher *tfm)
  89{
  90        int bsize = crypto_cipher_blocksize(tfm);
  91        unsigned int nbytes = walk->nbytes;
  92        u8 *src = walk->src.virt.addr;
  93        u8 *dst = walk->dst.virt.addr;
  94        u8 * const iv = walk->iv;
  95
  96        do {
  97                crypto_cipher_decrypt_one(tfm, dst, src);
  98                crypto_xor(dst, iv, bsize);
  99                crypto_xor_cpy(iv, dst, src, bsize);
 100
 101                src += bsize;
 102                dst += bsize;
 103        } while ((nbytes -= bsize) >= bsize);
 104
 105        return nbytes;
 106}
 107
 108static int crypto_pcbc_decrypt_inplace(struct skcipher_request *req,
 109                                       struct skcipher_walk *walk,
 110                                       struct crypto_cipher *tfm)
 111{
 112        int bsize = crypto_cipher_blocksize(tfm);
 113        unsigned int nbytes = walk->nbytes;
 114        u8 *src = walk->src.virt.addr;
 115        u8 * const iv = walk->iv;
 116        u8 tmpbuf[MAX_CIPHER_BLOCKSIZE] __aligned(__alignof__(u32));
 117
 118        do {
 119                memcpy(tmpbuf, src, bsize);
 120                crypto_cipher_decrypt_one(tfm, src, src);
 121                crypto_xor(src, iv, bsize);
 122                crypto_xor_cpy(iv, src, tmpbuf, bsize);
 123
 124                src += bsize;
 125        } while ((nbytes -= bsize) >= bsize);
 126
 127        return nbytes;
 128}
 129
 130static int crypto_pcbc_decrypt(struct skcipher_request *req)
 131{
 132        struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
 133        struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
 134        struct skcipher_walk walk;
 135        unsigned int nbytes;
 136        int err;
 137
 138        err = skcipher_walk_virt(&walk, req, false);
 139
 140        while ((nbytes = walk.nbytes)) {
 141                if (walk.src.virt.addr == walk.dst.virt.addr)
 142                        nbytes = crypto_pcbc_decrypt_inplace(req, &walk,
 143                                                             cipher);
 144                else
 145                        nbytes = crypto_pcbc_decrypt_segment(req, &walk,
 146                                                             cipher);
 147                err = skcipher_walk_done(&walk, nbytes);
 148        }
 149
 150        return err;
 151}
 152
 153static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb)
 154{
 155        struct skcipher_instance *inst;
 156        struct crypto_alg *alg;
 157        int err;
 158
 159        inst = skcipher_alloc_instance_simple(tmpl, tb, &alg);
 160        if (IS_ERR(inst))
 161                return PTR_ERR(inst);
 162
 163        inst->alg.encrypt = crypto_pcbc_encrypt;
 164        inst->alg.decrypt = crypto_pcbc_decrypt;
 165
 166        err = skcipher_register_instance(tmpl, inst);
 167        if (err)
 168                inst->free(inst);
 169        crypto_mod_put(alg);
 170        return err;
 171}
 172
 173static struct crypto_template crypto_pcbc_tmpl = {
 174        .name = "pcbc",
 175        .create = crypto_pcbc_create,
 176        .module = THIS_MODULE,
 177};
 178
 179static int __init crypto_pcbc_module_init(void)
 180{
 181        return crypto_register_template(&crypto_pcbc_tmpl);
 182}
 183
 184static void __exit crypto_pcbc_module_exit(void)
 185{
 186        crypto_unregister_template(&crypto_pcbc_tmpl);
 187}
 188
 189subsys_initcall(crypto_pcbc_module_init);
 190module_exit(crypto_pcbc_module_exit);
 191
 192MODULE_LICENSE("GPL");
 193MODULE_DESCRIPTION("PCBC block cipher mode of operation");
 194MODULE_ALIAS_CRYPTO("pcbc");
 195