12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2db131ef9SHerbert Xu /*
3db131ef9SHerbert Xu * CBC: Cipher Block Chaining mode
4db131ef9SHerbert Xu *
5cc868d82SHerbert Xu * Copyright (c) 2006-2016 Herbert Xu <herbert@gondor.apana.org.au>
6db131ef9SHerbert Xu */
7db131ef9SHerbert Xu
879c65d17SHerbert Xu #include <crypto/internal/skcipher.h>
9db131ef9SHerbert Xu #include <linux/err.h>
10db131ef9SHerbert Xu #include <linux/init.h>
11db131ef9SHerbert Xu #include <linux/kernel.h>
1250b6544eSHerbert Xu #include <linux/log2.h>
13db131ef9SHerbert Xu #include <linux/module.h>
14db131ef9SHerbert Xu
crypto_cbc_encrypt_segment(struct crypto_lskcipher * tfm,const u8 * src,u8 * dst,unsigned nbytes,u8 * iv)15705b52feSHerbert Xu static int crypto_cbc_encrypt_segment(struct crypto_lskcipher *tfm,
16705b52feSHerbert Xu const u8 *src, u8 *dst, unsigned nbytes,
17705b52feSHerbert Xu u8 *iv)
18db131ef9SHerbert Xu {
19705b52feSHerbert Xu unsigned int bsize = crypto_lskcipher_blocksize(tfm);
205f254dd4SHerbert Xu
21705b52feSHerbert Xu for (; nbytes >= bsize; src += bsize, dst += bsize, nbytes -= bsize) {
225f254dd4SHerbert Xu crypto_xor(iv, src, bsize);
23705b52feSHerbert Xu crypto_lskcipher_encrypt(tfm, iv, dst, bsize, NULL);
245f254dd4SHerbert Xu memcpy(iv, dst, bsize);
25705b52feSHerbert Xu }
265f254dd4SHerbert Xu
275f254dd4SHerbert Xu return nbytes;
285f254dd4SHerbert Xu }
295f254dd4SHerbert Xu
crypto_cbc_encrypt_inplace(struct crypto_lskcipher * tfm,u8 * src,unsigned nbytes,u8 * oiv)30705b52feSHerbert Xu static int crypto_cbc_encrypt_inplace(struct crypto_lskcipher *tfm,
31705b52feSHerbert Xu u8 *src, unsigned nbytes, u8 *oiv)
325f254dd4SHerbert Xu {
33705b52feSHerbert Xu unsigned int bsize = crypto_lskcipher_blocksize(tfm);
34705b52feSHerbert Xu u8 *iv = oiv;
355f254dd4SHerbert Xu
36705b52feSHerbert Xu if (nbytes < bsize)
37705b52feSHerbert Xu goto out;
385f254dd4SHerbert Xu
395f254dd4SHerbert Xu do {
405f254dd4SHerbert Xu crypto_xor(src, iv, bsize);
41705b52feSHerbert Xu crypto_lskcipher_encrypt(tfm, src, src, bsize, NULL);
425f254dd4SHerbert Xu iv = src;
435f254dd4SHerbert Xu
445f254dd4SHerbert Xu src += bsize;
455f254dd4SHerbert Xu } while ((nbytes -= bsize) >= bsize);
465f254dd4SHerbert Xu
47705b52feSHerbert Xu memcpy(oiv, iv, bsize);
485f254dd4SHerbert Xu
49705b52feSHerbert Xu out:
505f254dd4SHerbert Xu return nbytes;
5179c65d17SHerbert Xu }
5279c65d17SHerbert Xu
crypto_cbc_encrypt(struct crypto_lskcipher * tfm,const u8 * src,u8 * dst,unsigned len,u8 * iv,u32 flags)53705b52feSHerbert Xu static int crypto_cbc_encrypt(struct crypto_lskcipher *tfm, const u8 *src,
540ae4dcc1SHerbert Xu u8 *dst, unsigned len, u8 *iv, u32 flags)
5579c65d17SHerbert Xu {
56705b52feSHerbert Xu struct crypto_lskcipher **ctx = crypto_lskcipher_ctx(tfm);
570ae4dcc1SHerbert Xu bool final = flags & CRYPTO_LSKCIPHER_FLAG_FINAL;
58705b52feSHerbert Xu struct crypto_lskcipher *cipher = *ctx;
59705b52feSHerbert Xu int rem;
60db131ef9SHerbert Xu
61705b52feSHerbert Xu if (src == dst)
62705b52feSHerbert Xu rem = crypto_cbc_encrypt_inplace(cipher, dst, len, iv);
635f254dd4SHerbert Xu else
64705b52feSHerbert Xu rem = crypto_cbc_encrypt_segment(cipher, src, dst, len, iv);
65705b52feSHerbert Xu
66705b52feSHerbert Xu return rem && final ? -EINVAL : rem;
675f254dd4SHerbert Xu }
685f254dd4SHerbert Xu
crypto_cbc_decrypt_segment(struct crypto_lskcipher * tfm,const u8 * src,u8 * dst,unsigned nbytes,u8 * oiv)69705b52feSHerbert Xu static int crypto_cbc_decrypt_segment(struct crypto_lskcipher *tfm,
70705b52feSHerbert Xu const u8 *src, u8 *dst, unsigned nbytes,
71705b52feSHerbert Xu u8 *oiv)
725f254dd4SHerbert Xu {
73705b52feSHerbert Xu unsigned int bsize = crypto_lskcipher_blocksize(tfm);
74705b52feSHerbert Xu const u8 *iv = oiv;
755f254dd4SHerbert Xu
76705b52feSHerbert Xu if (nbytes < bsize)
77705b52feSHerbert Xu goto out;
785f254dd4SHerbert Xu
795f254dd4SHerbert Xu do {
80705b52feSHerbert Xu crypto_lskcipher_decrypt(tfm, src, dst, bsize, NULL);
815f254dd4SHerbert Xu crypto_xor(dst, iv, bsize);
825f254dd4SHerbert Xu iv = src;
835f254dd4SHerbert Xu
845f254dd4SHerbert Xu src += bsize;
855f254dd4SHerbert Xu dst += bsize;
865f254dd4SHerbert Xu } while ((nbytes -= bsize) >= bsize);
875f254dd4SHerbert Xu
88705b52feSHerbert Xu memcpy(oiv, iv, bsize);
895f254dd4SHerbert Xu
90705b52feSHerbert Xu out:
915f254dd4SHerbert Xu return nbytes;
925f254dd4SHerbert Xu }
935f254dd4SHerbert Xu
crypto_cbc_decrypt_inplace(struct crypto_lskcipher * tfm,u8 * src,unsigned nbytes,u8 * iv)94705b52feSHerbert Xu static int crypto_cbc_decrypt_inplace(struct crypto_lskcipher *tfm,
95705b52feSHerbert Xu u8 *src, unsigned nbytes, u8 *iv)
965f254dd4SHerbert Xu {
97705b52feSHerbert Xu unsigned int bsize = crypto_lskcipher_blocksize(tfm);
985f254dd4SHerbert Xu u8 last_iv[MAX_CIPHER_BLOCKSIZE];
995f254dd4SHerbert Xu
100705b52feSHerbert Xu if (nbytes < bsize)
101705b52feSHerbert Xu goto out;
1025f254dd4SHerbert Xu
1035f254dd4SHerbert Xu /* Start of the last block. */
1045f254dd4SHerbert Xu src += nbytes - (nbytes & (bsize - 1)) - bsize;
1055f254dd4SHerbert Xu memcpy(last_iv, src, bsize);
1065f254dd4SHerbert Xu
1075f254dd4SHerbert Xu for (;;) {
108705b52feSHerbert Xu crypto_lskcipher_decrypt(tfm, src, src, bsize, NULL);
1095f254dd4SHerbert Xu if ((nbytes -= bsize) < bsize)
1105f254dd4SHerbert Xu break;
1115f254dd4SHerbert Xu crypto_xor(src, src - bsize, bsize);
1125f254dd4SHerbert Xu src -= bsize;
1135f254dd4SHerbert Xu }
1145f254dd4SHerbert Xu
115705b52feSHerbert Xu crypto_xor(src, iv, bsize);
116705b52feSHerbert Xu memcpy(iv, last_iv, bsize);
1175f254dd4SHerbert Xu
118705b52feSHerbert Xu out:
1195f254dd4SHerbert Xu return nbytes;
1205f254dd4SHerbert Xu }
1215f254dd4SHerbert Xu
crypto_cbc_decrypt(struct crypto_lskcipher * tfm,const u8 * src,u8 * dst,unsigned len,u8 * iv,u32 flags)122705b52feSHerbert Xu static int crypto_cbc_decrypt(struct crypto_lskcipher *tfm, const u8 *src,
1230ae4dcc1SHerbert Xu u8 *dst, unsigned len, u8 *iv, u32 flags)
1245f254dd4SHerbert Xu {
125705b52feSHerbert Xu struct crypto_lskcipher **ctx = crypto_lskcipher_ctx(tfm);
1260ae4dcc1SHerbert Xu bool final = flags & CRYPTO_LSKCIPHER_FLAG_FINAL;
127705b52feSHerbert Xu struct crypto_lskcipher *cipher = *ctx;
128705b52feSHerbert Xu int rem;
1295f254dd4SHerbert Xu
130705b52feSHerbert Xu if (src == dst)
131705b52feSHerbert Xu rem = crypto_cbc_decrypt_inplace(cipher, dst, len, iv);
1325f254dd4SHerbert Xu else
133705b52feSHerbert Xu rem = crypto_cbc_decrypt_segment(cipher, src, dst, len, iv);
134db131ef9SHerbert Xu
135705b52feSHerbert Xu return rem && final ? -EINVAL : rem;
136db131ef9SHerbert Xu }
137db131ef9SHerbert Xu
crypto_cbc_create(struct crypto_template * tmpl,struct rtattr ** tb)13879c65d17SHerbert Xu static int crypto_cbc_create(struct crypto_template *tmpl, struct rtattr **tb)
13979c65d17SHerbert Xu {
140705b52feSHerbert Xu struct lskcipher_instance *inst;
141ebc610e5SHerbert Xu int err;
142db131ef9SHerbert Xu
143705b52feSHerbert Xu inst = lskcipher_alloc_instance_simple(tmpl, tb);
144a5a84a9dSEric Biggers if (IS_ERR(inst))
145a5a84a9dSEric Biggers return PTR_ERR(inst);
14679c65d17SHerbert Xu
14779c65d17SHerbert Xu err = -EINVAL;
148705b52feSHerbert Xu if (!is_power_of_2(inst->alg.co.base.cra_blocksize))
149a5a84a9dSEric Biggers goto out_free_inst;
15050b6544eSHerbert Xu
151*69fba378SHerbert Xu if (inst->alg.co.statesize)
152*69fba378SHerbert Xu goto out_free_inst;
153*69fba378SHerbert Xu
15479c65d17SHerbert Xu inst->alg.encrypt = crypto_cbc_encrypt;
15579c65d17SHerbert Xu inst->alg.decrypt = crypto_cbc_decrypt;
156db131ef9SHerbert Xu
157705b52feSHerbert Xu err = lskcipher_register_instance(tmpl, inst);
158b3c16bfcSHerbert Xu if (err) {
159a5a84a9dSEric Biggers out_free_inst:
160a5a84a9dSEric Biggers inst->free(inst);
161b3c16bfcSHerbert Xu }
162b3c16bfcSHerbert Xu
16379c65d17SHerbert Xu return err;
164db131ef9SHerbert Xu }
165db131ef9SHerbert Xu
166db131ef9SHerbert Xu static struct crypto_template crypto_cbc_tmpl = {
167db131ef9SHerbert Xu .name = "cbc",
16879c65d17SHerbert Xu .create = crypto_cbc_create,
169db131ef9SHerbert Xu .module = THIS_MODULE,
170db131ef9SHerbert Xu };
171db131ef9SHerbert Xu
crypto_cbc_module_init(void)172db131ef9SHerbert Xu static int __init crypto_cbc_module_init(void)
173db131ef9SHerbert Xu {
174db131ef9SHerbert Xu return crypto_register_template(&crypto_cbc_tmpl);
175db131ef9SHerbert Xu }
176db131ef9SHerbert Xu
crypto_cbc_module_exit(void)177db131ef9SHerbert Xu static void __exit crypto_cbc_module_exit(void)
178db131ef9SHerbert Xu {
179db131ef9SHerbert Xu crypto_unregister_template(&crypto_cbc_tmpl);
180db131ef9SHerbert Xu }
181db131ef9SHerbert Xu
182c4741b23SEric Biggers subsys_initcall(crypto_cbc_module_init);
183db131ef9SHerbert Xu module_exit(crypto_cbc_module_exit);
184db131ef9SHerbert Xu
185db131ef9SHerbert Xu MODULE_LICENSE("GPL");
186a5a84a9dSEric Biggers MODULE_DESCRIPTION("CBC block cipher mode of operation");
1874943ba16SKees Cook MODULE_ALIAS_CRYPTO("cbc");
188