xref: /openbmc/linux/crypto/pcbc.c (revision 2874c5fd284268364ece81a7bd936f3c8168e567)
1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
291652be5SDavid Howells /*
391652be5SDavid Howells  * PCBC: Propagating Cipher Block Chaining mode
491652be5SDavid Howells  *
591652be5SDavid Howells  * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
691652be5SDavid Howells  * Written by David Howells (dhowells@redhat.com)
791652be5SDavid Howells  *
891652be5SDavid Howells  * Derived from cbc.c
991652be5SDavid Howells  * - Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au>
1091652be5SDavid Howells  */
1191652be5SDavid Howells 
126650c4deSSalvatore Mesoraca #include <crypto/algapi.h>
13043a4400SHerbert Xu #include <crypto/internal/skcipher.h>
1491652be5SDavid Howells #include <linux/err.h>
1591652be5SDavid Howells #include <linux/init.h>
1691652be5SDavid Howells #include <linux/kernel.h>
1791652be5SDavid Howells #include <linux/module.h>
1891652be5SDavid Howells 
19043a4400SHerbert Xu static int crypto_pcbc_encrypt_segment(struct skcipher_request *req,
20043a4400SHerbert Xu 				       struct skcipher_walk *walk,
21d0b9007aSHerbert Xu 				       struct crypto_cipher *tfm)
2291652be5SDavid Howells {
2391652be5SDavid Howells 	int bsize = crypto_cipher_blocksize(tfm);
2491652be5SDavid Howells 	unsigned int nbytes = walk->nbytes;
2591652be5SDavid Howells 	u8 *src = walk->src.virt.addr;
2691652be5SDavid Howells 	u8 *dst = walk->dst.virt.addr;
27251b7aeaSEric Biggers 	u8 * const iv = walk->iv;
2891652be5SDavid Howells 
2991652be5SDavid Howells 	do {
30d0b9007aSHerbert Xu 		crypto_xor(iv, src, bsize);
31043a4400SHerbert Xu 		crypto_cipher_encrypt_one(tfm, dst, iv);
3245fe93dfSArd Biesheuvel 		crypto_xor_cpy(iv, dst, src, bsize);
3391652be5SDavid Howells 
3491652be5SDavid Howells 		src += bsize;
3591652be5SDavid Howells 		dst += bsize;
3691652be5SDavid Howells 	} while ((nbytes -= bsize) >= bsize);
3791652be5SDavid Howells 
3891652be5SDavid Howells 	return nbytes;
3991652be5SDavid Howells }
4091652be5SDavid Howells 
41043a4400SHerbert Xu static int crypto_pcbc_encrypt_inplace(struct skcipher_request *req,
42043a4400SHerbert Xu 				       struct skcipher_walk *walk,
43d0b9007aSHerbert Xu 				       struct crypto_cipher *tfm)
4491652be5SDavid Howells {
4591652be5SDavid Howells 	int bsize = crypto_cipher_blocksize(tfm);
4691652be5SDavid Howells 	unsigned int nbytes = walk->nbytes;
4791652be5SDavid Howells 	u8 *src = walk->src.virt.addr;
48251b7aeaSEric Biggers 	u8 * const iv = walk->iv;
496650c4deSSalvatore Mesoraca 	u8 tmpbuf[MAX_CIPHER_BLOCKSIZE];
5091652be5SDavid Howells 
5191652be5SDavid Howells 	do {
5291652be5SDavid Howells 		memcpy(tmpbuf, src, bsize);
53d0b9007aSHerbert Xu 		crypto_xor(iv, src, bsize);
54043a4400SHerbert Xu 		crypto_cipher_encrypt_one(tfm, src, iv);
5545fe93dfSArd Biesheuvel 		crypto_xor_cpy(iv, tmpbuf, src, bsize);
5691652be5SDavid Howells 
5791652be5SDavid Howells 		src += bsize;
5891652be5SDavid Howells 	} while ((nbytes -= bsize) >= bsize);
5991652be5SDavid Howells 
6091652be5SDavid Howells 	return nbytes;
6191652be5SDavid Howells }
6291652be5SDavid Howells 
63043a4400SHerbert Xu static int crypto_pcbc_encrypt(struct skcipher_request *req)
6491652be5SDavid Howells {
65043a4400SHerbert Xu 	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
660be487baSEric Biggers 	struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
67043a4400SHerbert Xu 	struct skcipher_walk walk;
68043a4400SHerbert Xu 	unsigned int nbytes;
6991652be5SDavid Howells 	int err;
7091652be5SDavid Howells 
71043a4400SHerbert Xu 	err = skcipher_walk_virt(&walk, req, false);
7291652be5SDavid Howells 
7391652be5SDavid Howells 	while ((nbytes = walk.nbytes)) {
7491652be5SDavid Howells 		if (walk.src.virt.addr == walk.dst.virt.addr)
75043a4400SHerbert Xu 			nbytes = crypto_pcbc_encrypt_inplace(req, &walk,
760be487baSEric Biggers 							     cipher);
7791652be5SDavid Howells 		else
78043a4400SHerbert Xu 			nbytes = crypto_pcbc_encrypt_segment(req, &walk,
790be487baSEric Biggers 							     cipher);
80043a4400SHerbert Xu 		err = skcipher_walk_done(&walk, nbytes);
8191652be5SDavid Howells 	}
8291652be5SDavid Howells 
8391652be5SDavid Howells 	return err;
8491652be5SDavid Howells }
8591652be5SDavid Howells 
86043a4400SHerbert Xu static int crypto_pcbc_decrypt_segment(struct skcipher_request *req,
87043a4400SHerbert Xu 				       struct skcipher_walk *walk,
88d0b9007aSHerbert Xu 				       struct crypto_cipher *tfm)
8991652be5SDavid Howells {
9091652be5SDavid Howells 	int bsize = crypto_cipher_blocksize(tfm);
9191652be5SDavid Howells 	unsigned int nbytes = walk->nbytes;
9291652be5SDavid Howells 	u8 *src = walk->src.virt.addr;
9391652be5SDavid Howells 	u8 *dst = walk->dst.virt.addr;
94251b7aeaSEric Biggers 	u8 * const iv = walk->iv;
9591652be5SDavid Howells 
9691652be5SDavid Howells 	do {
97043a4400SHerbert Xu 		crypto_cipher_decrypt_one(tfm, dst, src);
98d0b9007aSHerbert Xu 		crypto_xor(dst, iv, bsize);
9945fe93dfSArd Biesheuvel 		crypto_xor_cpy(iv, dst, src, bsize);
10091652be5SDavid Howells 
10191652be5SDavid Howells 		src += bsize;
10291652be5SDavid Howells 		dst += bsize;
10391652be5SDavid Howells 	} while ((nbytes -= bsize) >= bsize);
10491652be5SDavid Howells 
10591652be5SDavid Howells 	return nbytes;
10691652be5SDavid Howells }
10791652be5SDavid Howells 
108043a4400SHerbert Xu static int crypto_pcbc_decrypt_inplace(struct skcipher_request *req,
109043a4400SHerbert Xu 				       struct skcipher_walk *walk,
110d0b9007aSHerbert Xu 				       struct crypto_cipher *tfm)
11191652be5SDavid Howells {
11291652be5SDavid Howells 	int bsize = crypto_cipher_blocksize(tfm);
11391652be5SDavid Howells 	unsigned int nbytes = walk->nbytes;
11491652be5SDavid Howells 	u8 *src = walk->src.virt.addr;
115251b7aeaSEric Biggers 	u8 * const iv = walk->iv;
1166650c4deSSalvatore Mesoraca 	u8 tmpbuf[MAX_CIPHER_BLOCKSIZE] __aligned(__alignof__(u32));
11791652be5SDavid Howells 
11891652be5SDavid Howells 	do {
11991652be5SDavid Howells 		memcpy(tmpbuf, src, bsize);
120043a4400SHerbert Xu 		crypto_cipher_decrypt_one(tfm, src, src);
121d0b9007aSHerbert Xu 		crypto_xor(src, iv, bsize);
12245fe93dfSArd Biesheuvel 		crypto_xor_cpy(iv, src, tmpbuf, bsize);
12391652be5SDavid Howells 
12491652be5SDavid Howells 		src += bsize;
12591652be5SDavid Howells 	} while ((nbytes -= bsize) >= bsize);
12691652be5SDavid Howells 
12791652be5SDavid Howells 	return nbytes;
12891652be5SDavid Howells }
12991652be5SDavid Howells 
130043a4400SHerbert Xu static int crypto_pcbc_decrypt(struct skcipher_request *req)
13191652be5SDavid Howells {
132043a4400SHerbert Xu 	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
1330be487baSEric Biggers 	struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
134043a4400SHerbert Xu 	struct skcipher_walk walk;
135043a4400SHerbert Xu 	unsigned int nbytes;
13691652be5SDavid Howells 	int err;
13791652be5SDavid Howells 
138043a4400SHerbert Xu 	err = skcipher_walk_virt(&walk, req, false);
13991652be5SDavid Howells 
14091652be5SDavid Howells 	while ((nbytes = walk.nbytes)) {
14191652be5SDavid Howells 		if (walk.src.virt.addr == walk.dst.virt.addr)
142043a4400SHerbert Xu 			nbytes = crypto_pcbc_decrypt_inplace(req, &walk,
1430be487baSEric Biggers 							     cipher);
14491652be5SDavid Howells 		else
145043a4400SHerbert Xu 			nbytes = crypto_pcbc_decrypt_segment(req, &walk,
1460be487baSEric Biggers 							     cipher);
147043a4400SHerbert Xu 		err = skcipher_walk_done(&walk, nbytes);
14891652be5SDavid Howells 	}
14991652be5SDavid Howells 
15091652be5SDavid Howells 	return err;
15191652be5SDavid Howells }
15291652be5SDavid Howells 
153043a4400SHerbert Xu static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb)
154043a4400SHerbert Xu {
155043a4400SHerbert Xu 	struct skcipher_instance *inst;
15691652be5SDavid Howells 	struct crypto_alg *alg;
157ebc610e5SHerbert Xu 	int err;
15891652be5SDavid Howells 
1590be487baSEric Biggers 	inst = skcipher_alloc_instance_simple(tmpl, tb, &alg);
1600be487baSEric Biggers 	if (IS_ERR(inst))
1610be487baSEric Biggers 		return PTR_ERR(inst);
162ebc610e5SHerbert Xu 
163043a4400SHerbert Xu 	inst->alg.encrypt = crypto_pcbc_encrypt;
164043a4400SHerbert Xu 	inst->alg.decrypt = crypto_pcbc_decrypt;
16591652be5SDavid Howells 
166043a4400SHerbert Xu 	err = skcipher_register_instance(tmpl, inst);
167043a4400SHerbert Xu 	if (err)
1680be487baSEric Biggers 		inst->free(inst);
169e5bde04cSPan Bian 	crypto_mod_put(alg);
170043a4400SHerbert Xu 	return err;
17191652be5SDavid Howells }
17291652be5SDavid Howells 
17391652be5SDavid Howells static struct crypto_template crypto_pcbc_tmpl = {
17491652be5SDavid Howells 	.name = "pcbc",
175043a4400SHerbert Xu 	.create = crypto_pcbc_create,
17691652be5SDavid Howells 	.module = THIS_MODULE,
17791652be5SDavid Howells };
17891652be5SDavid Howells 
17991652be5SDavid Howells static int __init crypto_pcbc_module_init(void)
18091652be5SDavid Howells {
18191652be5SDavid Howells 	return crypto_register_template(&crypto_pcbc_tmpl);
18291652be5SDavid Howells }
18391652be5SDavid Howells 
18491652be5SDavid Howells static void __exit crypto_pcbc_module_exit(void)
18591652be5SDavid Howells {
18691652be5SDavid Howells 	crypto_unregister_template(&crypto_pcbc_tmpl);
18791652be5SDavid Howells }
18891652be5SDavid Howells 
189c4741b23SEric Biggers subsys_initcall(crypto_pcbc_module_init);
19091652be5SDavid Howells module_exit(crypto_pcbc_module_exit);
19191652be5SDavid Howells 
19291652be5SDavid Howells MODULE_LICENSE("GPL");
1930be487baSEric Biggers MODULE_DESCRIPTION("PCBC block cipher mode of operation");
1944943ba16SKees Cook MODULE_ALIAS_CRYPTO("pcbc");
195