linux/crypto/cbc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * CBC: Cipher Block Chaining mode
   4 *
   5 * Copyright (c) 2006-2016 Herbert Xu <herbert@gondor.apana.org.au>
   6 */
   7
   8#include <crypto/internal/skcipher.h>
   9#include <linux/err.h>
  10#include <linux/init.h>
  11#include <linux/kernel.h>
  12#include <linux/log2.h>
  13#include <linux/module.h>
  14
  15static int crypto_cbc_encrypt_segment(struct crypto_lskcipher *tfm,
  16                                      const u8 *src, u8 *dst, unsigned nbytes,
  17                                      u8 *iv)
  18{
  19        unsigned int bsize = crypto_lskcipher_blocksize(tfm);
  20
  21        for (; nbytes >= bsize; src += bsize, dst += bsize, nbytes -= bsize) {
  22                crypto_xor(iv, src, bsize);
  23                crypto_lskcipher_encrypt(tfm, iv, dst, bsize, NULL);
  24                memcpy(iv, dst, bsize);
  25        }
  26
  27        return nbytes;
  28}
  29
  30static int crypto_cbc_encrypt_inplace(struct crypto_lskcipher *tfm,
  31                                      u8 *src, unsigned nbytes, u8 *oiv)
  32{
  33        unsigned int bsize = crypto_lskcipher_blocksize(tfm);
  34        u8 *iv = oiv;
  35
  36        if (nbytes < bsize)
  37                goto out;
  38
  39        do {
  40                crypto_xor(src, iv, bsize);
  41                crypto_lskcipher_encrypt(tfm, src, src, bsize, NULL);
  42                iv = src;
  43
  44                src += bsize;
  45        } while ((nbytes -= bsize) >= bsize);
  46
  47        memcpy(oiv, iv, bsize);
  48
  49out:
  50        return nbytes;
  51}
  52
  53static int crypto_cbc_encrypt(struct crypto_lskcipher *tfm, const u8 *src,
  54                              u8 *dst, unsigned len, u8 *iv, bool final)
  55{
  56        struct crypto_lskcipher **ctx = crypto_lskcipher_ctx(tfm);
  57        struct crypto_lskcipher *cipher = *ctx;
  58        int rem;
  59
  60        if (src == dst)
  61                rem = crypto_cbc_encrypt_inplace(cipher, dst, len, iv);
  62        else
  63                rem = crypto_cbc_encrypt_segment(cipher, src, dst, len, iv);
  64
  65        return rem && final ? -EINVAL : rem;
  66}
  67
  68static int crypto_cbc_decrypt_segment(struct crypto_lskcipher *tfm,
  69                                      const u8 *src, u8 *dst, unsigned nbytes,
  70                                      u8 *oiv)
  71{
  72        unsigned int bsize = crypto_lskcipher_blocksize(tfm);
  73        const u8 *iv = oiv;
  74
  75        if (nbytes < bsize)
  76                goto out;
  77
  78        do {
  79                crypto_lskcipher_decrypt(tfm, src, dst, bsize, NULL);
  80                crypto_xor(dst, iv, bsize);
  81                iv = src;
  82
  83                src += bsize;
  84                dst += bsize;
  85        } while ((nbytes -= bsize) >= bsize);
  86
  87        memcpy(oiv, iv, bsize);
  88
  89out:
  90        return nbytes;
  91}
  92
  93static int crypto_cbc_decrypt_inplace(struct crypto_lskcipher *tfm,
  94                                      u8 *src, unsigned nbytes, u8 *iv)
  95{
  96        unsigned int bsize = crypto_lskcipher_blocksize(tfm);
  97        u8 last_iv[MAX_CIPHER_BLOCKSIZE];
  98
  99        if (nbytes < bsize)
 100                goto out;
 101
 102        /* Start of the last block. */
 103        src += nbytes - (nbytes & (bsize - 1)) - bsize;
 104        memcpy(last_iv, src, bsize);
 105
 106        for (;;) {
 107                crypto_lskcipher_decrypt(tfm, src, src, bsize, NULL);
 108                if ((nbytes -= bsize) < bsize)
 109                        break;
 110                crypto_xor(src, src - bsize, bsize);
 111                src -= bsize;
 112        }
 113
 114        crypto_xor(src, iv, bsize);
 115        memcpy(iv, last_iv, bsize);
 116
 117out:
 118        return nbytes;
 119}
 120
 121static int crypto_cbc_decrypt(struct crypto_lskcipher *tfm, const u8 *src,
 122                              u8 *dst, unsigned len, u8 *iv, bool final)
 123{
 124        struct crypto_lskcipher **ctx = crypto_lskcipher_ctx(tfm);
 125        struct crypto_lskcipher *cipher = *ctx;
 126        int rem;
 127
 128        if (src == dst)
 129                rem = crypto_cbc_decrypt_inplace(cipher, dst, len, iv);
 130        else
 131                rem = crypto_cbc_decrypt_segment(cipher, src, dst, len, iv);
 132
 133        return rem && final ? -EINVAL : rem;
 134}
 135
 136static int crypto_cbc_create(struct crypto_template *tmpl, struct rtattr **tb)
 137{
 138        struct lskcipher_instance *inst;
 139        int err;
 140
 141        inst = lskcipher_alloc_instance_simple(tmpl, tb);
 142        if (IS_ERR(inst))
 143                return PTR_ERR(inst);
 144
 145        err = -EINVAL;
 146        if (!is_power_of_2(inst->alg.co.base.cra_blocksize))
 147                goto out_free_inst;
 148
 149        inst->alg.encrypt = crypto_cbc_encrypt;
 150        inst->alg.decrypt = crypto_cbc_decrypt;
 151
 152        err = lskcipher_register_instance(tmpl, inst);
 153        if (err) {
 154out_free_inst:
 155                inst->free(inst);
 156        }
 157
 158        return err;
 159}
 160
 161static struct crypto_template crypto_cbc_tmpl = {
 162        .name = "cbc",
 163        .create = crypto_cbc_create,
 164        .module = THIS_MODULE,
 165};
 166
 167static int __init crypto_cbc_module_init(void)
 168{
 169        return crypto_register_template(&crypto_cbc_tmpl);
 170}
 171
 172static void __exit crypto_cbc_module_exit(void)
 173{
 174        crypto_unregister_template(&crypto_cbc_tmpl);
 175}
 176
 177subsys_initcall(crypto_cbc_module_init);
 178module_exit(crypto_cbc_module_exit);
 179
 180MODULE_LICENSE("GPL");
 181MODULE_DESCRIPTION("CBC block cipher mode of operation");
 182MODULE_ALIAS_CRYPTO("cbc");
 183