xref: /openbmc/linux/crypto/cbc.c (revision 0eb76ba2)
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 
8e6c2e65cSMarcelo Cerri #include <crypto/algapi.h>
9*0eb76ba2SArd Biesheuvel #include <crypto/internal/cipher.h>
1079c65d17SHerbert Xu #include <crypto/internal/skcipher.h>
11db131ef9SHerbert Xu #include <linux/err.h>
12db131ef9SHerbert Xu #include <linux/init.h>
13db131ef9SHerbert Xu #include <linux/kernel.h>
1450b6544eSHerbert Xu #include <linux/log2.h>
15db131ef9SHerbert Xu #include <linux/module.h>
16db131ef9SHerbert Xu 
crypto_cbc_encrypt_segment(struct skcipher_walk * walk,struct crypto_skcipher * skcipher)175f254dd4SHerbert Xu static int crypto_cbc_encrypt_segment(struct skcipher_walk *walk,
185f254dd4SHerbert Xu 				      struct crypto_skcipher *skcipher)
19db131ef9SHerbert Xu {
205f254dd4SHerbert Xu 	unsigned int bsize = crypto_skcipher_blocksize(skcipher);
215f254dd4SHerbert Xu 	void (*fn)(struct crypto_tfm *, u8 *, const u8 *);
225f254dd4SHerbert Xu 	unsigned int nbytes = walk->nbytes;
235f254dd4SHerbert Xu 	u8 *src = walk->src.virt.addr;
245f254dd4SHerbert Xu 	u8 *dst = walk->dst.virt.addr;
255f254dd4SHerbert Xu 	struct crypto_cipher *cipher;
265f254dd4SHerbert Xu 	struct crypto_tfm *tfm;
275f254dd4SHerbert Xu 	u8 *iv = walk->iv;
285f254dd4SHerbert Xu 
295f254dd4SHerbert Xu 	cipher = skcipher_cipher_simple(skcipher);
305f254dd4SHerbert Xu 	tfm = crypto_cipher_tfm(cipher);
315f254dd4SHerbert Xu 	fn = crypto_cipher_alg(cipher)->cia_encrypt;
325f254dd4SHerbert Xu 
335f254dd4SHerbert Xu 	do {
345f254dd4SHerbert Xu 		crypto_xor(iv, src, bsize);
355f254dd4SHerbert Xu 		fn(tfm, dst, iv);
365f254dd4SHerbert Xu 		memcpy(iv, dst, bsize);
375f254dd4SHerbert Xu 
385f254dd4SHerbert Xu 		src += bsize;
395f254dd4SHerbert Xu 		dst += bsize;
405f254dd4SHerbert Xu 	} while ((nbytes -= bsize) >= bsize);
415f254dd4SHerbert Xu 
425f254dd4SHerbert Xu 	return nbytes;
435f254dd4SHerbert Xu }
445f254dd4SHerbert Xu 
crypto_cbc_encrypt_inplace(struct skcipher_walk * walk,struct crypto_skcipher * skcipher)455f254dd4SHerbert Xu static int crypto_cbc_encrypt_inplace(struct skcipher_walk *walk,
465f254dd4SHerbert Xu 				      struct crypto_skcipher *skcipher)
475f254dd4SHerbert Xu {
485f254dd4SHerbert Xu 	unsigned int bsize = crypto_skcipher_blocksize(skcipher);
495f254dd4SHerbert Xu 	void (*fn)(struct crypto_tfm *, u8 *, const u8 *);
505f254dd4SHerbert Xu 	unsigned int nbytes = walk->nbytes;
515f254dd4SHerbert Xu 	u8 *src = walk->src.virt.addr;
525f254dd4SHerbert Xu 	struct crypto_cipher *cipher;
535f254dd4SHerbert Xu 	struct crypto_tfm *tfm;
545f254dd4SHerbert Xu 	u8 *iv = walk->iv;
555f254dd4SHerbert Xu 
565f254dd4SHerbert Xu 	cipher = skcipher_cipher_simple(skcipher);
575f254dd4SHerbert Xu 	tfm = crypto_cipher_tfm(cipher);
585f254dd4SHerbert Xu 	fn = crypto_cipher_alg(cipher)->cia_encrypt;
595f254dd4SHerbert Xu 
605f254dd4SHerbert Xu 	do {
615f254dd4SHerbert Xu 		crypto_xor(src, iv, bsize);
625f254dd4SHerbert Xu 		fn(tfm, src, src);
635f254dd4SHerbert Xu 		iv = src;
645f254dd4SHerbert Xu 
655f254dd4SHerbert Xu 		src += bsize;
665f254dd4SHerbert Xu 	} while ((nbytes -= bsize) >= bsize);
675f254dd4SHerbert Xu 
685f254dd4SHerbert Xu 	memcpy(walk->iv, iv, bsize);
695f254dd4SHerbert Xu 
705f254dd4SHerbert Xu 	return nbytes;
7179c65d17SHerbert Xu }
7279c65d17SHerbert Xu 
crypto_cbc_encrypt(struct skcipher_request * req)7379c65d17SHerbert Xu static int crypto_cbc_encrypt(struct skcipher_request *req)
7479c65d17SHerbert Xu {
755f254dd4SHerbert Xu 	struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req);
7679c65d17SHerbert Xu 	struct skcipher_walk walk;
77db131ef9SHerbert Xu 	int err;
78db131ef9SHerbert Xu 
7979c65d17SHerbert Xu 	err = skcipher_walk_virt(&walk, req, false);
80db131ef9SHerbert Xu 
8179c65d17SHerbert Xu 	while (walk.nbytes) {
825f254dd4SHerbert Xu 		if (walk.src.virt.addr == walk.dst.virt.addr)
835f254dd4SHerbert Xu 			err = crypto_cbc_encrypt_inplace(&walk, skcipher);
845f254dd4SHerbert Xu 		else
855f254dd4SHerbert Xu 			err = crypto_cbc_encrypt_segment(&walk, skcipher);
865f254dd4SHerbert Xu 		err = skcipher_walk_done(&walk, err);
875f254dd4SHerbert Xu 	}
885f254dd4SHerbert Xu 
895f254dd4SHerbert Xu 	return err;
905f254dd4SHerbert Xu }
915f254dd4SHerbert Xu 
crypto_cbc_decrypt_segment(struct skcipher_walk * walk,struct crypto_skcipher * skcipher)925f254dd4SHerbert Xu static int crypto_cbc_decrypt_segment(struct skcipher_walk *walk,
935f254dd4SHerbert Xu 				      struct crypto_skcipher *skcipher)
945f254dd4SHerbert Xu {
955f254dd4SHerbert Xu 	unsigned int bsize = crypto_skcipher_blocksize(skcipher);
965f254dd4SHerbert Xu 	void (*fn)(struct crypto_tfm *, u8 *, const u8 *);
975f254dd4SHerbert Xu 	unsigned int nbytes = walk->nbytes;
985f254dd4SHerbert Xu 	u8 *src = walk->src.virt.addr;
995f254dd4SHerbert Xu 	u8 *dst = walk->dst.virt.addr;
1005f254dd4SHerbert Xu 	struct crypto_cipher *cipher;
1015f254dd4SHerbert Xu 	struct crypto_tfm *tfm;
1025f254dd4SHerbert Xu 	u8 *iv = walk->iv;
1035f254dd4SHerbert Xu 
1045f254dd4SHerbert Xu 	cipher = skcipher_cipher_simple(skcipher);
1055f254dd4SHerbert Xu 	tfm = crypto_cipher_tfm(cipher);
1065f254dd4SHerbert Xu 	fn = crypto_cipher_alg(cipher)->cia_decrypt;
1075f254dd4SHerbert Xu 
1085f254dd4SHerbert Xu 	do {
1095f254dd4SHerbert Xu 		fn(tfm, dst, src);
1105f254dd4SHerbert Xu 		crypto_xor(dst, iv, bsize);
1115f254dd4SHerbert Xu 		iv = src;
1125f254dd4SHerbert Xu 
1135f254dd4SHerbert Xu 		src += bsize;
1145f254dd4SHerbert Xu 		dst += bsize;
1155f254dd4SHerbert Xu 	} while ((nbytes -= bsize) >= bsize);
1165f254dd4SHerbert Xu 
1175f254dd4SHerbert Xu 	memcpy(walk->iv, iv, bsize);
1185f254dd4SHerbert Xu 
1195f254dd4SHerbert Xu 	return nbytes;
1205f254dd4SHerbert Xu }
1215f254dd4SHerbert Xu 
crypto_cbc_decrypt_inplace(struct skcipher_walk * walk,struct crypto_skcipher * skcipher)1225f254dd4SHerbert Xu static int crypto_cbc_decrypt_inplace(struct skcipher_walk *walk,
1235f254dd4SHerbert Xu 				      struct crypto_skcipher *skcipher)
1245f254dd4SHerbert Xu {
1255f254dd4SHerbert Xu 	unsigned int bsize = crypto_skcipher_blocksize(skcipher);
1265f254dd4SHerbert Xu 	void (*fn)(struct crypto_tfm *, u8 *, const u8 *);
1275f254dd4SHerbert Xu 	unsigned int nbytes = walk->nbytes;
1285f254dd4SHerbert Xu 	u8 *src = walk->src.virt.addr;
1295f254dd4SHerbert Xu 	u8 last_iv[MAX_CIPHER_BLOCKSIZE];
1305f254dd4SHerbert Xu 	struct crypto_cipher *cipher;
1315f254dd4SHerbert Xu 	struct crypto_tfm *tfm;
1325f254dd4SHerbert Xu 
1335f254dd4SHerbert Xu 	cipher = skcipher_cipher_simple(skcipher);
1345f254dd4SHerbert Xu 	tfm = crypto_cipher_tfm(cipher);
1355f254dd4SHerbert Xu 	fn = crypto_cipher_alg(cipher)->cia_decrypt;
1365f254dd4SHerbert Xu 
1375f254dd4SHerbert Xu 	/* Start of the last block. */
1385f254dd4SHerbert Xu 	src += nbytes - (nbytes & (bsize - 1)) - bsize;
1395f254dd4SHerbert Xu 	memcpy(last_iv, src, bsize);
1405f254dd4SHerbert Xu 
1415f254dd4SHerbert Xu 	for (;;) {
1425f254dd4SHerbert Xu 		fn(tfm, src, src);
1435f254dd4SHerbert Xu 		if ((nbytes -= bsize) < bsize)
1445f254dd4SHerbert Xu 			break;
1455f254dd4SHerbert Xu 		crypto_xor(src, src - bsize, bsize);
1465f254dd4SHerbert Xu 		src -= bsize;
1475f254dd4SHerbert Xu 	}
1485f254dd4SHerbert Xu 
1495f254dd4SHerbert Xu 	crypto_xor(src, walk->iv, bsize);
1505f254dd4SHerbert Xu 	memcpy(walk->iv, last_iv, bsize);
1515f254dd4SHerbert Xu 
1525f254dd4SHerbert Xu 	return nbytes;
1535f254dd4SHerbert Xu }
1545f254dd4SHerbert Xu 
crypto_cbc_decrypt(struct skcipher_request * req)1555f254dd4SHerbert Xu static int crypto_cbc_decrypt(struct skcipher_request *req)
1565f254dd4SHerbert Xu {
1575f254dd4SHerbert Xu 	struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req);
1585f254dd4SHerbert Xu 	struct skcipher_walk walk;
1595f254dd4SHerbert Xu 	int err;
1605f254dd4SHerbert Xu 
1615f254dd4SHerbert Xu 	err = skcipher_walk_virt(&walk, req, false);
1625f254dd4SHerbert Xu 
1635f254dd4SHerbert Xu 	while (walk.nbytes) {
1645f254dd4SHerbert Xu 		if (walk.src.virt.addr == walk.dst.virt.addr)
1655f254dd4SHerbert Xu 			err = crypto_cbc_decrypt_inplace(&walk, skcipher);
1665f254dd4SHerbert Xu 		else
1675f254dd4SHerbert Xu 			err = crypto_cbc_decrypt_segment(&walk, skcipher);
16879c65d17SHerbert Xu 		err = skcipher_walk_done(&walk, err);
169db131ef9SHerbert Xu 	}
170db131ef9SHerbert Xu 
171db131ef9SHerbert Xu 	return err;
172db131ef9SHerbert Xu }
173db131ef9SHerbert Xu 
crypto_cbc_create(struct crypto_template * tmpl,struct rtattr ** tb)17479c65d17SHerbert Xu static int crypto_cbc_create(struct crypto_template *tmpl, struct rtattr **tb)
17579c65d17SHerbert Xu {
17679c65d17SHerbert Xu 	struct skcipher_instance *inst;
177db131ef9SHerbert Xu 	struct crypto_alg *alg;
178ebc610e5SHerbert Xu 	int err;
179db131ef9SHerbert Xu 
180b3c16bfcSHerbert Xu 	inst = skcipher_alloc_instance_simple(tmpl, tb);
181a5a84a9dSEric Biggers 	if (IS_ERR(inst))
182a5a84a9dSEric Biggers 		return PTR_ERR(inst);
18379c65d17SHerbert Xu 
184b3c16bfcSHerbert Xu 	alg = skcipher_ialg_simple(inst);
185b3c16bfcSHerbert Xu 
18679c65d17SHerbert Xu 	err = -EINVAL;
18750b6544eSHerbert Xu 	if (!is_power_of_2(alg->cra_blocksize))
188a5a84a9dSEric Biggers 		goto out_free_inst;
18950b6544eSHerbert Xu 
19079c65d17SHerbert Xu 	inst->alg.encrypt = crypto_cbc_encrypt;
19179c65d17SHerbert Xu 	inst->alg.decrypt = crypto_cbc_decrypt;
192db131ef9SHerbert Xu 
19379c65d17SHerbert Xu 	err = skcipher_register_instance(tmpl, inst);
194b3c16bfcSHerbert Xu 	if (err) {
195a5a84a9dSEric Biggers out_free_inst:
196a5a84a9dSEric Biggers 		inst->free(inst);
197b3c16bfcSHerbert Xu 	}
198b3c16bfcSHerbert Xu 
19979c65d17SHerbert Xu 	return err;
200db131ef9SHerbert Xu }
201db131ef9SHerbert Xu 
202db131ef9SHerbert Xu static struct crypto_template crypto_cbc_tmpl = {
203db131ef9SHerbert Xu 	.name = "cbc",
20479c65d17SHerbert Xu 	.create = crypto_cbc_create,
205db131ef9SHerbert Xu 	.module = THIS_MODULE,
206db131ef9SHerbert Xu };
207db131ef9SHerbert Xu 
crypto_cbc_module_init(void)208db131ef9SHerbert Xu static int __init crypto_cbc_module_init(void)
209db131ef9SHerbert Xu {
210db131ef9SHerbert Xu 	return crypto_register_template(&crypto_cbc_tmpl);
211db131ef9SHerbert Xu }
212db131ef9SHerbert Xu 
crypto_cbc_module_exit(void)213db131ef9SHerbert Xu static void __exit crypto_cbc_module_exit(void)
214db131ef9SHerbert Xu {
215db131ef9SHerbert Xu 	crypto_unregister_template(&crypto_cbc_tmpl);
216db131ef9SHerbert Xu }
217db131ef9SHerbert Xu 
218c4741b23SEric Biggers subsys_initcall(crypto_cbc_module_init);
219db131ef9SHerbert Xu module_exit(crypto_cbc_module_exit);
220db131ef9SHerbert Xu 
221db131ef9SHerbert Xu MODULE_LICENSE("GPL");
222a5a84a9dSEric Biggers MODULE_DESCRIPTION("CBC block cipher mode of operation");
2234943ba16SKees Cook MODULE_ALIAS_CRYPTO("cbc");
224