diff options
Diffstat (limited to 'crypto/cbc.c')
| -rw-r--r-- | crypto/cbc.c | 240 |
1 files changed, 109 insertions, 131 deletions
diff --git a/crypto/cbc.c b/crypto/cbc.c index b761b1f9c6ca..ed3df6246765 100644 --- a/crypto/cbc.c +++ b/crypto/cbc.c @@ -1,188 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * CBC: Cipher Block Chaining mode * * Copyright (c) 2006-2016 Herbert Xu <herbert@gondor.apana.org.au> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * */ -#include <crypto/algapi.h> -#include <crypto/cbc.h> #include <crypto/internal/skcipher.h> #include <linux/err.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/log2.h> #include <linux/module.h> -#include <linux/slab.h> -struct crypto_cbc_ctx { - struct crypto_cipher *child; -}; - -static int crypto_cbc_setkey(struct crypto_skcipher *parent, const u8 *key, - unsigned int keylen) +static int crypto_cbc_encrypt_segment(struct crypto_lskcipher *tfm, + const u8 *src, u8 *dst, unsigned nbytes, + u8 *iv) { - struct crypto_cbc_ctx *ctx = crypto_skcipher_ctx(parent); - struct crypto_cipher *child = ctx->child; - int err; + unsigned int bsize = crypto_lskcipher_blocksize(tfm); - crypto_cipher_clear_flags(child, CRYPTO_TFM_REQ_MASK); - crypto_cipher_set_flags(child, crypto_skcipher_get_flags(parent) & - CRYPTO_TFM_REQ_MASK); - err = crypto_cipher_setkey(child, key, keylen); - crypto_skcipher_set_flags(parent, crypto_cipher_get_flags(child) & - CRYPTO_TFM_RES_MASK); - return err; + for (; nbytes >= bsize; src += bsize, dst += bsize, nbytes -= bsize) { + crypto_xor(iv, src, bsize); + crypto_lskcipher_encrypt(tfm, iv, dst, bsize, NULL); + memcpy(iv, dst, bsize); + } + + return nbytes; } -static inline void crypto_cbc_encrypt_one(struct crypto_skcipher *tfm, - const u8 *src, u8 *dst) +static int crypto_cbc_encrypt_inplace(struct crypto_lskcipher *tfm, + u8 *src, unsigned nbytes, u8 *oiv) { - struct crypto_cbc_ctx *ctx = crypto_skcipher_ctx(tfm); + unsigned int bsize = crypto_lskcipher_blocksize(tfm); + u8 *iv = oiv; - crypto_cipher_encrypt_one(ctx->child, dst, src); -} + if (nbytes < bsize) + goto out; -static int crypto_cbc_encrypt(struct skcipher_request *req) -{ - return crypto_cbc_encrypt_walk(req, crypto_cbc_encrypt_one); + do { + crypto_xor(src, iv, bsize); + crypto_lskcipher_encrypt(tfm, src, src, bsize, NULL); + iv = src; + + src += bsize; + } while ((nbytes -= bsize) >= bsize); + + memcpy(oiv, iv, bsize); + +out: + return nbytes; } -static inline void crypto_cbc_decrypt_one(struct crypto_skcipher *tfm, - const u8 *src, u8 *dst) +static int crypto_cbc_encrypt(struct crypto_lskcipher *tfm, const u8 *src, + u8 *dst, unsigned len, u8 *iv, u32 flags) { - struct crypto_cbc_ctx *ctx = crypto_skcipher_ctx(tfm); + struct crypto_lskcipher **ctx = crypto_lskcipher_ctx(tfm); + bool final = flags & CRYPTO_LSKCIPHER_FLAG_FINAL; + struct crypto_lskcipher *cipher = *ctx; + int rem; + + if (src == dst) + rem = crypto_cbc_encrypt_inplace(cipher, dst, len, iv); + else + rem = crypto_cbc_encrypt_segment(cipher, src, dst, len, iv); - crypto_cipher_decrypt_one(ctx->child, dst, src); + return rem && final ? -EINVAL : rem; } -static int crypto_cbc_decrypt(struct skcipher_request *req) +static int crypto_cbc_decrypt_segment(struct crypto_lskcipher *tfm, + const u8 *src, u8 *dst, unsigned nbytes, + u8 *oiv) { - struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); - struct skcipher_walk walk; - int err; + unsigned int bsize = crypto_lskcipher_blocksize(tfm); + const u8 *iv = oiv; - err = skcipher_walk_virt(&walk, req, false); + if (nbytes < bsize) + goto out; - while (walk.nbytes) { - err = crypto_cbc_decrypt_blocks(&walk, tfm, - crypto_cbc_decrypt_one); - err = skcipher_walk_done(&walk, err); - } + do { + crypto_lskcipher_decrypt(tfm, src, dst, bsize, NULL); + crypto_xor(dst, iv, bsize); + iv = src; - return err; + src += bsize; + dst += bsize; + } while ((nbytes -= bsize) >= bsize); + + memcpy(oiv, iv, bsize); + +out: + return nbytes; } -static int crypto_cbc_init_tfm(struct crypto_skcipher *tfm) +static int crypto_cbc_decrypt_inplace(struct crypto_lskcipher *tfm, + u8 *src, unsigned nbytes, u8 *iv) { - struct skcipher_instance *inst = skcipher_alg_instance(tfm); - struct crypto_spawn *spawn = skcipher_instance_ctx(inst); - struct crypto_cbc_ctx *ctx = crypto_skcipher_ctx(tfm); - struct crypto_cipher *cipher; + unsigned int bsize = crypto_lskcipher_blocksize(tfm); + u8 last_iv[MAX_CIPHER_BLOCKSIZE]; + + if (nbytes < bsize) + goto out; + + /* Start of the last block. */ + src += nbytes - (nbytes & (bsize - 1)) - bsize; + memcpy(last_iv, src, bsize); + + for (;;) { + crypto_lskcipher_decrypt(tfm, src, src, bsize, NULL); + if ((nbytes -= bsize) < bsize) + break; + crypto_xor(src, src - bsize, bsize); + src -= bsize; + } - cipher = crypto_spawn_cipher(spawn); - if (IS_ERR(cipher)) - return PTR_ERR(cipher); + crypto_xor(src, iv, bsize); + memcpy(iv, last_iv, bsize); - ctx->child = cipher; - return 0; +out: + return nbytes; } -static void crypto_cbc_exit_tfm(struct crypto_skcipher *tfm) +static int crypto_cbc_decrypt(struct crypto_lskcipher *tfm, const u8 *src, + u8 *dst, unsigned len, u8 *iv, u32 flags) { - struct crypto_cbc_ctx *ctx = crypto_skcipher_ctx(tfm); + struct crypto_lskcipher **ctx = crypto_lskcipher_ctx(tfm); + bool final = flags & CRYPTO_LSKCIPHER_FLAG_FINAL; + struct crypto_lskcipher *cipher = *ctx; + int rem; - crypto_free_cipher(ctx->child); -} + if (src == dst) + rem = crypto_cbc_decrypt_inplace(cipher, dst, len, iv); + else + rem = crypto_cbc_decrypt_segment(cipher, src, dst, len, iv); -static void crypto_cbc_free(struct skcipher_instance *inst) -{ - crypto_drop_skcipher(skcipher_instance_ctx(inst)); - kfree(inst); + return rem && final ? -EINVAL : rem; } static int crypto_cbc_create(struct crypto_template *tmpl, struct rtattr **tb) { - struct skcipher_instance *inst; - struct crypto_attr_type *algt; - struct crypto_spawn *spawn; - struct crypto_alg *alg; - u32 mask; + struct lskcipher_instance *inst; int err; - err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_SKCIPHER); - if (err) - return err; - - inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL); - if (!inst) - return -ENOMEM; - - algt = crypto_get_attr_type(tb); - err = PTR_ERR(algt); - if (IS_ERR(algt)) - goto err_free_inst; - - mask = CRYPTO_ALG_TYPE_MASK | - crypto_requires_off(algt->type, algt->mask, - CRYPTO_ALG_NEED_FALLBACK); - - alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER, mask); - err = PTR_ERR(alg); - if (IS_ERR(alg)) - goto err_free_inst; - - spawn = skcipher_instance_ctx(inst); - err = crypto_init_spawn(spawn, alg, skcipher_crypto_instance(inst), - CRYPTO_ALG_TYPE_MASK); - crypto_mod_put(alg); - if (err) - goto err_free_inst; - - err = crypto_inst_setname(skcipher_crypto_instance(inst), "cbc", alg); - if (err) - goto err_drop_spawn; + inst = lskcipher_alloc_instance_simple(tmpl, tb); + if (IS_ERR(inst)) + return PTR_ERR(inst); err = -EINVAL; - if (!is_power_of_2(alg->cra_blocksize)) - goto err_drop_spawn; - - inst->alg.base.cra_priority = alg->cra_priority; - inst->alg.base.cra_blocksize = alg->cra_blocksize; - inst->alg.base.cra_alignmask = alg->cra_alignmask; - - inst->alg.ivsize = alg->cra_blocksize; - inst->alg.min_keysize = alg->cra_cipher.cia_min_keysize; - inst->alg.max_keysize = alg->cra_cipher.cia_max_keysize; - - inst->alg.base.cra_ctxsize = sizeof(struct crypto_cbc_ctx); + if (!is_power_of_2(inst->alg.co.base.cra_blocksize)) + goto out_free_inst; - inst->alg.init = crypto_cbc_init_tfm; - inst->alg.exit = crypto_cbc_exit_tfm; + if (inst->alg.co.statesize) + goto out_free_inst; - inst->alg.setkey = crypto_cbc_setkey; inst->alg.encrypt = crypto_cbc_encrypt; inst->alg.decrypt = crypto_cbc_decrypt; - inst->free = crypto_cbc_free; - - err = skcipher_register_instance(tmpl, inst); - if (err) - goto err_drop_spawn; + err = lskcipher_register_instance(tmpl, inst); + if (err) { +out_free_inst: + inst->free(inst); + } -out: return err; - -err_drop_spawn: - crypto_drop_spawn(spawn); -err_free_inst: - kfree(inst); - goto out; } static struct crypto_template crypto_cbc_tmpl = { @@ -205,5 +183,5 @@ module_init(crypto_cbc_module_init); module_exit(crypto_cbc_module_exit); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("CBC block cipher algorithm"); +MODULE_DESCRIPTION("CBC block cipher mode of operation"); MODULE_ALIAS_CRYPTO("cbc"); |
