12874c5fdSThomas 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>
13*0eb76ba2SArd Biesheuvel #include <crypto/internal/cipher.h>
14043a4400SHerbert Xu #include <crypto/internal/skcipher.h>
1591652be5SDavid Howells #include <linux/err.h>
1691652be5SDavid Howells #include <linux/init.h>
1791652be5SDavid Howells #include <linux/kernel.h>
1891652be5SDavid Howells #include <linux/module.h>
1991652be5SDavid Howells
crypto_pcbc_encrypt_segment(struct skcipher_request * req,struct skcipher_walk * walk,struct crypto_cipher * tfm)20043a4400SHerbert Xu static int crypto_pcbc_encrypt_segment(struct skcipher_request *req,
21043a4400SHerbert Xu struct skcipher_walk *walk,
22d0b9007aSHerbert Xu struct crypto_cipher *tfm)
2391652be5SDavid Howells {
2491652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm);
2591652be5SDavid Howells unsigned int nbytes = walk->nbytes;
2691652be5SDavid Howells u8 *src = walk->src.virt.addr;
2791652be5SDavid Howells u8 *dst = walk->dst.virt.addr;
28251b7aeaSEric Biggers u8 * const iv = walk->iv;
2991652be5SDavid Howells
3091652be5SDavid Howells do {
31d0b9007aSHerbert Xu crypto_xor(iv, src, bsize);
32043a4400SHerbert Xu crypto_cipher_encrypt_one(tfm, dst, iv);
3345fe93dfSArd Biesheuvel crypto_xor_cpy(iv, dst, src, bsize);
3491652be5SDavid Howells
3591652be5SDavid Howells src += bsize;
3691652be5SDavid Howells dst += bsize;
3791652be5SDavid Howells } while ((nbytes -= bsize) >= bsize);
3891652be5SDavid Howells
3991652be5SDavid Howells return nbytes;
4091652be5SDavid Howells }
4191652be5SDavid Howells
crypto_pcbc_encrypt_inplace(struct skcipher_request * req,struct skcipher_walk * walk,struct crypto_cipher * tfm)42043a4400SHerbert Xu static int crypto_pcbc_encrypt_inplace(struct skcipher_request *req,
43043a4400SHerbert Xu struct skcipher_walk *walk,
44d0b9007aSHerbert Xu struct crypto_cipher *tfm)
4591652be5SDavid Howells {
4691652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm);
4791652be5SDavid Howells unsigned int nbytes = walk->nbytes;
4891652be5SDavid Howells u8 *src = walk->src.virt.addr;
49251b7aeaSEric Biggers u8 * const iv = walk->iv;
506650c4deSSalvatore Mesoraca u8 tmpbuf[MAX_CIPHER_BLOCKSIZE];
5191652be5SDavid Howells
5291652be5SDavid Howells do {
5391652be5SDavid Howells memcpy(tmpbuf, src, bsize);
54d0b9007aSHerbert Xu crypto_xor(iv, src, bsize);
55043a4400SHerbert Xu crypto_cipher_encrypt_one(tfm, src, iv);
5645fe93dfSArd Biesheuvel crypto_xor_cpy(iv, tmpbuf, src, bsize);
5791652be5SDavid Howells
5891652be5SDavid Howells src += bsize;
5991652be5SDavid Howells } while ((nbytes -= bsize) >= bsize);
6091652be5SDavid Howells
6191652be5SDavid Howells return nbytes;
6291652be5SDavid Howells }
6391652be5SDavid Howells
crypto_pcbc_encrypt(struct skcipher_request * req)64043a4400SHerbert Xu static int crypto_pcbc_encrypt(struct skcipher_request *req)
6591652be5SDavid Howells {
66043a4400SHerbert Xu struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
670be487baSEric Biggers struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
68043a4400SHerbert Xu struct skcipher_walk walk;
69043a4400SHerbert Xu unsigned int nbytes;
7091652be5SDavid Howells int err;
7191652be5SDavid Howells
72043a4400SHerbert Xu err = skcipher_walk_virt(&walk, req, false);
7391652be5SDavid Howells
7491652be5SDavid Howells while ((nbytes = walk.nbytes)) {
7591652be5SDavid Howells if (walk.src.virt.addr == walk.dst.virt.addr)
76043a4400SHerbert Xu nbytes = crypto_pcbc_encrypt_inplace(req, &walk,
770be487baSEric Biggers cipher);
7891652be5SDavid Howells else
79043a4400SHerbert Xu nbytes = crypto_pcbc_encrypt_segment(req, &walk,
800be487baSEric Biggers cipher);
81043a4400SHerbert Xu err = skcipher_walk_done(&walk, nbytes);
8291652be5SDavid Howells }
8391652be5SDavid Howells
8491652be5SDavid Howells return err;
8591652be5SDavid Howells }
8691652be5SDavid Howells
crypto_pcbc_decrypt_segment(struct skcipher_request * req,struct skcipher_walk * walk,struct crypto_cipher * tfm)87043a4400SHerbert Xu static int crypto_pcbc_decrypt_segment(struct skcipher_request *req,
88043a4400SHerbert Xu struct skcipher_walk *walk,
89d0b9007aSHerbert Xu struct crypto_cipher *tfm)
9091652be5SDavid Howells {
9191652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm);
9291652be5SDavid Howells unsigned int nbytes = walk->nbytes;
9391652be5SDavid Howells u8 *src = walk->src.virt.addr;
9491652be5SDavid Howells u8 *dst = walk->dst.virt.addr;
95251b7aeaSEric Biggers u8 * const iv = walk->iv;
9691652be5SDavid Howells
9791652be5SDavid Howells do {
98043a4400SHerbert Xu crypto_cipher_decrypt_one(tfm, dst, src);
99d0b9007aSHerbert Xu crypto_xor(dst, iv, bsize);
10045fe93dfSArd Biesheuvel crypto_xor_cpy(iv, dst, src, bsize);
10191652be5SDavid Howells
10291652be5SDavid Howells src += bsize;
10391652be5SDavid Howells dst += bsize;
10491652be5SDavid Howells } while ((nbytes -= bsize) >= bsize);
10591652be5SDavid Howells
10691652be5SDavid Howells return nbytes;
10791652be5SDavid Howells }
10891652be5SDavid Howells
crypto_pcbc_decrypt_inplace(struct skcipher_request * req,struct skcipher_walk * walk,struct crypto_cipher * tfm)109043a4400SHerbert Xu static int crypto_pcbc_decrypt_inplace(struct skcipher_request *req,
110043a4400SHerbert Xu struct skcipher_walk *walk,
111d0b9007aSHerbert Xu struct crypto_cipher *tfm)
11291652be5SDavid Howells {
11391652be5SDavid Howells int bsize = crypto_cipher_blocksize(tfm);
11491652be5SDavid Howells unsigned int nbytes = walk->nbytes;
11591652be5SDavid Howells u8 *src = walk->src.virt.addr;
116251b7aeaSEric Biggers u8 * const iv = walk->iv;
1176650c4deSSalvatore Mesoraca u8 tmpbuf[MAX_CIPHER_BLOCKSIZE] __aligned(__alignof__(u32));
11891652be5SDavid Howells
11991652be5SDavid Howells do {
12091652be5SDavid Howells memcpy(tmpbuf, src, bsize);
121043a4400SHerbert Xu crypto_cipher_decrypt_one(tfm, src, src);
122d0b9007aSHerbert Xu crypto_xor(src, iv, bsize);
12345fe93dfSArd Biesheuvel crypto_xor_cpy(iv, src, tmpbuf, bsize);
12491652be5SDavid Howells
12591652be5SDavid Howells src += bsize;
12691652be5SDavid Howells } while ((nbytes -= bsize) >= bsize);
12791652be5SDavid Howells
12891652be5SDavid Howells return nbytes;
12991652be5SDavid Howells }
13091652be5SDavid Howells
crypto_pcbc_decrypt(struct skcipher_request * req)131043a4400SHerbert Xu static int crypto_pcbc_decrypt(struct skcipher_request *req)
13291652be5SDavid Howells {
133043a4400SHerbert Xu struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
1340be487baSEric Biggers struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
135043a4400SHerbert Xu struct skcipher_walk walk;
136043a4400SHerbert Xu unsigned int nbytes;
13791652be5SDavid Howells int err;
13891652be5SDavid Howells
139043a4400SHerbert Xu err = skcipher_walk_virt(&walk, req, false);
14091652be5SDavid Howells
14191652be5SDavid Howells while ((nbytes = walk.nbytes)) {
14291652be5SDavid Howells if (walk.src.virt.addr == walk.dst.virt.addr)
143043a4400SHerbert Xu nbytes = crypto_pcbc_decrypt_inplace(req, &walk,
1440be487baSEric Biggers cipher);
14591652be5SDavid Howells else
146043a4400SHerbert Xu nbytes = crypto_pcbc_decrypt_segment(req, &walk,
1470be487baSEric Biggers cipher);
148043a4400SHerbert Xu err = skcipher_walk_done(&walk, nbytes);
14991652be5SDavid Howells }
15091652be5SDavid Howells
15191652be5SDavid Howells return err;
15291652be5SDavid Howells }
15391652be5SDavid Howells
crypto_pcbc_create(struct crypto_template * tmpl,struct rtattr ** tb)154043a4400SHerbert Xu static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb)
155043a4400SHerbert Xu {
156043a4400SHerbert Xu struct skcipher_instance *inst;
157ebc610e5SHerbert Xu int err;
15891652be5SDavid Howells
159b3c16bfcSHerbert Xu inst = skcipher_alloc_instance_simple(tmpl, tb);
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);
169b3c16bfcSHerbert Xu
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
crypto_pcbc_module_init(void)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
crypto_pcbc_module_exit(void)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*0eb76ba2SArd Biesheuvel MODULE_IMPORT_NS(CRYPTO_INTERNAL);
196