xref: /openbmc/linux/crypto/pcbc.c (revision 0be487ba2e2f1593f7274b04615367d8830f6461)
191652be5SDavid Howells /*
291652be5SDavid Howells  * PCBC: Propagating Cipher Block Chaining mode
391652be5SDavid Howells  *
491652be5SDavid Howells  * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
591652be5SDavid Howells  * Written by David Howells (dhowells@redhat.com)
691652be5SDavid Howells  *
791652be5SDavid Howells  * Derived from cbc.c
891652be5SDavid Howells  * - Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au>
991652be5SDavid Howells  *
1091652be5SDavid Howells  * This program is free software; you can redistribute it and/or modify it
1191652be5SDavid Howells  * under the terms of the GNU General Public License as published by the Free
1291652be5SDavid Howells  * Software Foundation; either version 2 of the License, or (at your option)
1391652be5SDavid Howells  * any later version.
1491652be5SDavid Howells  *
1591652be5SDavid Howells  */
1691652be5SDavid Howells 
176650c4deSSalvatore Mesoraca #include <crypto/algapi.h>
18043a4400SHerbert Xu #include <crypto/internal/skcipher.h>
1991652be5SDavid Howells #include <linux/err.h>
2091652be5SDavid Howells #include <linux/init.h>
2191652be5SDavid Howells #include <linux/kernel.h>
2291652be5SDavid Howells #include <linux/module.h>
2391652be5SDavid Howells 
24043a4400SHerbert Xu static int crypto_pcbc_encrypt_segment(struct skcipher_request *req,
25043a4400SHerbert Xu 				       struct skcipher_walk *walk,
26d0b9007aSHerbert Xu 				       struct crypto_cipher *tfm)
2791652be5SDavid Howells {
2891652be5SDavid Howells 	int bsize = crypto_cipher_blocksize(tfm);
2991652be5SDavid Howells 	unsigned int nbytes = walk->nbytes;
3091652be5SDavid Howells 	u8 *src = walk->src.virt.addr;
3191652be5SDavid Howells 	u8 *dst = walk->dst.virt.addr;
32251b7aeaSEric Biggers 	u8 * const iv = walk->iv;
3391652be5SDavid Howells 
3491652be5SDavid Howells 	do {
35d0b9007aSHerbert Xu 		crypto_xor(iv, src, bsize);
36043a4400SHerbert Xu 		crypto_cipher_encrypt_one(tfm, dst, iv);
3745fe93dfSArd Biesheuvel 		crypto_xor_cpy(iv, dst, src, bsize);
3891652be5SDavid Howells 
3991652be5SDavid Howells 		src += bsize;
4091652be5SDavid Howells 		dst += bsize;
4191652be5SDavid Howells 	} while ((nbytes -= bsize) >= bsize);
4291652be5SDavid Howells 
4391652be5SDavid Howells 	return nbytes;
4491652be5SDavid Howells }
4591652be5SDavid Howells 
46043a4400SHerbert Xu static int crypto_pcbc_encrypt_inplace(struct skcipher_request *req,
47043a4400SHerbert Xu 				       struct skcipher_walk *walk,
48d0b9007aSHerbert Xu 				       struct crypto_cipher *tfm)
4991652be5SDavid Howells {
5091652be5SDavid Howells 	int bsize = crypto_cipher_blocksize(tfm);
5191652be5SDavid Howells 	unsigned int nbytes = walk->nbytes;
5291652be5SDavid Howells 	u8 *src = walk->src.virt.addr;
53251b7aeaSEric Biggers 	u8 * const iv = walk->iv;
546650c4deSSalvatore Mesoraca 	u8 tmpbuf[MAX_CIPHER_BLOCKSIZE];
5591652be5SDavid Howells 
5691652be5SDavid Howells 	do {
5791652be5SDavid Howells 		memcpy(tmpbuf, src, bsize);
58d0b9007aSHerbert Xu 		crypto_xor(iv, src, bsize);
59043a4400SHerbert Xu 		crypto_cipher_encrypt_one(tfm, src, iv);
6045fe93dfSArd Biesheuvel 		crypto_xor_cpy(iv, tmpbuf, src, bsize);
6191652be5SDavid Howells 
6291652be5SDavid Howells 		src += bsize;
6391652be5SDavid Howells 	} while ((nbytes -= bsize) >= bsize);
6491652be5SDavid Howells 
6591652be5SDavid Howells 	return nbytes;
6691652be5SDavid Howells }
6791652be5SDavid Howells 
68043a4400SHerbert Xu static int crypto_pcbc_encrypt(struct skcipher_request *req)
6991652be5SDavid Howells {
70043a4400SHerbert Xu 	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
71*0be487baSEric Biggers 	struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
72043a4400SHerbert Xu 	struct skcipher_walk walk;
73043a4400SHerbert Xu 	unsigned int nbytes;
7491652be5SDavid Howells 	int err;
7591652be5SDavid Howells 
76043a4400SHerbert Xu 	err = skcipher_walk_virt(&walk, req, false);
7791652be5SDavid Howells 
7891652be5SDavid Howells 	while ((nbytes = walk.nbytes)) {
7991652be5SDavid Howells 		if (walk.src.virt.addr == walk.dst.virt.addr)
80043a4400SHerbert Xu 			nbytes = crypto_pcbc_encrypt_inplace(req, &walk,
81*0be487baSEric Biggers 							     cipher);
8291652be5SDavid Howells 		else
83043a4400SHerbert Xu 			nbytes = crypto_pcbc_encrypt_segment(req, &walk,
84*0be487baSEric Biggers 							     cipher);
85043a4400SHerbert Xu 		err = skcipher_walk_done(&walk, nbytes);
8691652be5SDavid Howells 	}
8791652be5SDavid Howells 
8891652be5SDavid Howells 	return err;
8991652be5SDavid Howells }
9091652be5SDavid Howells 
91043a4400SHerbert Xu static int crypto_pcbc_decrypt_segment(struct skcipher_request *req,
92043a4400SHerbert Xu 				       struct skcipher_walk *walk,
93d0b9007aSHerbert Xu 				       struct crypto_cipher *tfm)
9491652be5SDavid Howells {
9591652be5SDavid Howells 	int bsize = crypto_cipher_blocksize(tfm);
9691652be5SDavid Howells 	unsigned int nbytes = walk->nbytes;
9791652be5SDavid Howells 	u8 *src = walk->src.virt.addr;
9891652be5SDavid Howells 	u8 *dst = walk->dst.virt.addr;
99251b7aeaSEric Biggers 	u8 * const iv = walk->iv;
10091652be5SDavid Howells 
10191652be5SDavid Howells 	do {
102043a4400SHerbert Xu 		crypto_cipher_decrypt_one(tfm, dst, src);
103d0b9007aSHerbert Xu 		crypto_xor(dst, iv, bsize);
10445fe93dfSArd Biesheuvel 		crypto_xor_cpy(iv, dst, src, bsize);
10591652be5SDavid Howells 
10691652be5SDavid Howells 		src += bsize;
10791652be5SDavid Howells 		dst += bsize;
10891652be5SDavid Howells 	} while ((nbytes -= bsize) >= bsize);
10991652be5SDavid Howells 
11091652be5SDavid Howells 	return nbytes;
11191652be5SDavid Howells }
11291652be5SDavid Howells 
113043a4400SHerbert Xu static int crypto_pcbc_decrypt_inplace(struct skcipher_request *req,
114043a4400SHerbert Xu 				       struct skcipher_walk *walk,
115d0b9007aSHerbert Xu 				       struct crypto_cipher *tfm)
11691652be5SDavid Howells {
11791652be5SDavid Howells 	int bsize = crypto_cipher_blocksize(tfm);
11891652be5SDavid Howells 	unsigned int nbytes = walk->nbytes;
11991652be5SDavid Howells 	u8 *src = walk->src.virt.addr;
120251b7aeaSEric Biggers 	u8 * const iv = walk->iv;
1216650c4deSSalvatore Mesoraca 	u8 tmpbuf[MAX_CIPHER_BLOCKSIZE] __aligned(__alignof__(u32));
12291652be5SDavid Howells 
12391652be5SDavid Howells 	do {
12491652be5SDavid Howells 		memcpy(tmpbuf, src, bsize);
125043a4400SHerbert Xu 		crypto_cipher_decrypt_one(tfm, src, src);
126d0b9007aSHerbert Xu 		crypto_xor(src, iv, bsize);
12745fe93dfSArd Biesheuvel 		crypto_xor_cpy(iv, src, tmpbuf, bsize);
12891652be5SDavid Howells 
12991652be5SDavid Howells 		src += bsize;
13091652be5SDavid Howells 	} while ((nbytes -= bsize) >= bsize);
13191652be5SDavid Howells 
13291652be5SDavid Howells 	return nbytes;
13391652be5SDavid Howells }
13491652be5SDavid Howells 
135043a4400SHerbert Xu static int crypto_pcbc_decrypt(struct skcipher_request *req)
13691652be5SDavid Howells {
137043a4400SHerbert Xu 	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
138*0be487baSEric Biggers 	struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
139043a4400SHerbert Xu 	struct skcipher_walk walk;
140043a4400SHerbert Xu 	unsigned int nbytes;
14191652be5SDavid Howells 	int err;
14291652be5SDavid Howells 
143043a4400SHerbert Xu 	err = skcipher_walk_virt(&walk, req, false);
14491652be5SDavid Howells 
14591652be5SDavid Howells 	while ((nbytes = walk.nbytes)) {
14691652be5SDavid Howells 		if (walk.src.virt.addr == walk.dst.virt.addr)
147043a4400SHerbert Xu 			nbytes = crypto_pcbc_decrypt_inplace(req, &walk,
148*0be487baSEric Biggers 							     cipher);
14991652be5SDavid Howells 		else
150043a4400SHerbert Xu 			nbytes = crypto_pcbc_decrypt_segment(req, &walk,
151*0be487baSEric Biggers 							     cipher);
152043a4400SHerbert Xu 		err = skcipher_walk_done(&walk, nbytes);
15391652be5SDavid Howells 	}
15491652be5SDavid Howells 
15591652be5SDavid Howells 	return err;
15691652be5SDavid Howells }
15791652be5SDavid Howells 
158043a4400SHerbert Xu static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb)
159043a4400SHerbert Xu {
160043a4400SHerbert Xu 	struct skcipher_instance *inst;
16191652be5SDavid Howells 	struct crypto_alg *alg;
162ebc610e5SHerbert Xu 	int err;
16391652be5SDavid Howells 
164*0be487baSEric Biggers 	inst = skcipher_alloc_instance_simple(tmpl, tb, &alg);
165*0be487baSEric Biggers 	if (IS_ERR(inst))
166*0be487baSEric Biggers 		return PTR_ERR(inst);
167ebc610e5SHerbert Xu 
168043a4400SHerbert Xu 	inst->alg.encrypt = crypto_pcbc_encrypt;
169043a4400SHerbert Xu 	inst->alg.decrypt = crypto_pcbc_decrypt;
17091652be5SDavid Howells 
171043a4400SHerbert Xu 	err = skcipher_register_instance(tmpl, inst);
172043a4400SHerbert Xu 	if (err)
173*0be487baSEric Biggers 		inst->free(inst);
174e5bde04cSPan Bian 	crypto_mod_put(alg);
175043a4400SHerbert Xu 	return err;
17691652be5SDavid Howells }
17791652be5SDavid Howells 
17891652be5SDavid Howells static struct crypto_template crypto_pcbc_tmpl = {
17991652be5SDavid Howells 	.name = "pcbc",
180043a4400SHerbert Xu 	.create = crypto_pcbc_create,
18191652be5SDavid Howells 	.module = THIS_MODULE,
18291652be5SDavid Howells };
18391652be5SDavid Howells 
18491652be5SDavid Howells static int __init crypto_pcbc_module_init(void)
18591652be5SDavid Howells {
18691652be5SDavid Howells 	return crypto_register_template(&crypto_pcbc_tmpl);
18791652be5SDavid Howells }
18891652be5SDavid Howells 
18991652be5SDavid Howells static void __exit crypto_pcbc_module_exit(void)
19091652be5SDavid Howells {
19191652be5SDavid Howells 	crypto_unregister_template(&crypto_pcbc_tmpl);
19291652be5SDavid Howells }
19391652be5SDavid Howells 
19491652be5SDavid Howells module_init(crypto_pcbc_module_init);
19591652be5SDavid Howells module_exit(crypto_pcbc_module_exit);
19691652be5SDavid Howells 
19791652be5SDavid Howells MODULE_LICENSE("GPL");
198*0be487baSEric Biggers MODULE_DESCRIPTION("PCBC block cipher mode of operation");
1994943ba16SKees Cook MODULE_ALIAS_CRYPTO("pcbc");
200