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/cipher.h>
  14#include <crypto/internal/skcipher.h>
  15#include <linux/err.h>
  16#include <linux/init.h>
  17#include <linux/kernel.h>
  18#include <linux/module.h>
  19
  20static int crypto_pcbc_encrypt_segment(struct skcipher_request *req,
  21                                       struct skcipher_walk *walk,
  22                                       struct crypto_cipher *tfm)
  23{
  24        int bsize = crypto_cipher_blocksize(tfm);
  25        unsigned int nbytes = walk->nbytes;
  26        u8 *src = walk->src.virt.addr;
  27        u8 *dst = walk->dst.virt.addr;
  28        u8 * const iv = walk->iv;
  29
  30        do {
  31                crypto_xor(iv, src, bsize);
  32                crypto_cipher_encrypt_one(tfm, dst, iv);
  33                crypto_xor_cpy(iv, dst, src, bsize);
  34
  35                src += bsize;
  36                dst += bsize;
  37        } while ((nbytes -= bsize) >= bsize);
  38
  39        return nbytes;
  40}
  41
  42static int crypto_pcbc_encrypt_inplace(struct skcipher_request *req,
  43                                       struct skcipher_walk *walk,
  44                                       struct crypto_cipher *tfm)
  45{
  46        int bsize = crypto_cipher_blocksize(tfm);
  47        unsigned int nbytes = walk->nbytes;
  48        u8 *src = walk->src.virt.addr;
  49        u8 * const iv = walk->iv;
  50        u8 tmpbuf[MAX_CIPHER_BLOCKSIZE];
  51
  52        do {
  53                memcpy(tmpbuf, src, bsize);
  54                crypto_xor(iv, src, bsize);
  55                crypto_cipher_encrypt_one(tfm, src, iv);
  56                crypto_xor_cpy(iv, tmpbuf, src, bsize);
  57
  58                src += bsize;
  59        } while ((nbytes -= bsize) >= bsize);
  60
  61        return nbytes;
  62}
  63
  64static int crypto_pcbc_encrypt(struct skcipher_request *req)
  65{
  66        struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
  67        struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
  68        struct skcipher_walk walk;
  69        unsigned int nbytes;
  70        int err;
  71
  72        err = skcipher_walk_virt(&walk, req, false);
  73
  74        while ((nbytes = walk.nbytes)) {
  75                if (walk.src.virt.addr == walk.dst.virt.addr)
  76                        nbytes = crypto_pcbc_encrypt_inplace(req, &walk,
  77                                                             cipher);
  78                else
  79                        nbytes = crypto_pcbc_encrypt_segment(req, &walk,
  80                                                             cipher);
  81                err = skcipher_walk_done(&walk, nbytes);
  82        }
  83
  84        return err;
  85}
  86
  87static int crypto_pcbc_decrypt_segment(struct skcipher_request *req,
  88                                       struct skcipher_walk *walk,
  89                                       struct crypto_cipher *tfm)
  90{
  91        int bsize = crypto_cipher_blocksize(tfm);
  92        unsigned int nbytes = walk->nbytes;
  93        u8 *src = walk->src.virt.addr;
  94        u8 *dst = walk->dst.virt.addr;
  95        u8 * const iv = walk->iv;
  96
  97        do {
  98                crypto_cipher_decrypt_one(tfm, dst, src);
  99                crypto_xor(dst, iv, bsize);
 100                crypto_xor_cpy(iv, dst, src, bsize);
 101
 102                src += bsize;
 103                dst += bsize;
 104        } while ((nbytes -= bsize) >= bsize);
 105
 106        return nbytes;
 107}
 108
 109static int crypto_pcbc_decrypt_inplace(struct skcipher_request *req,
 110                                       struct skcipher_walk *walk,
 111                                       struct crypto_cipher *tfm)
 112{
 113        int bsize = crypto_cipher_blocksize(tfm);
 114        unsigned int nbytes = walk->nbytes;
 115        u8 *src = walk->src.virt.addr;
 116        u8 * const iv = walk->iv;
 117        u8 tmpbuf[MAX_CIPHER_BLOCKSIZE] __aligned(__alignof__(u32));
 118
 119        do {
 120                memcpy(tmpbuf, src, bsize);
 121                crypto_cipher_decrypt_one(tfm, src, src);
 122                crypto_xor(src, iv, bsize);
 123                crypto_xor_cpy(iv, src, tmpbuf, bsize);
 124
 125                src += bsize;
 126        } while ((nbytes -= bsize) >= bsize);
 127
 128        return nbytes;
 129}
 130
 131static int crypto_pcbc_decrypt(struct skcipher_request *req)
 132{
 133        struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
 134        struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
 135        struct skcipher_walk walk;
 136        unsigned int nbytes;
 137        int err;
 138
 139        err = skcipher_walk_virt(&walk, req, false);
 140
 141        while ((nbytes = walk.nbytes)) {
 142                if (walk.src.virt.addr == walk.dst.virt.addr)
 143                        nbytes = crypto_pcbc_decrypt_inplace(req, &walk,
 144                                                             cipher);
 145                else
 146                        nbytes = crypto_pcbc_decrypt_segment(req, &walk,
 147                                                             cipher);
 148                err = skcipher_walk_done(&walk, nbytes);
 149        }
 150
 151        return err;
 152}
 153
 154static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb)
 155{
 156        struct skcipher_instance *inst;
 157        int err;
 158
 159        inst = skcipher_alloc_instance_simple(tmpl, tb);
 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
 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");
 195MODULE_IMPORT_NS(CRYPTO_INTERNAL);
 196