xref: /linux/crypto/cbc.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
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