blob: 7030f59e46b6bc2760491176268b6df0cd8b7ea0 [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
David Howells91652be2006-12-16 12:09:02 +11002/*
3 * PCBC: Propagating Cipher Block Chaining mode
4 *
5 * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
6 * Written by David Howells (dhowells@redhat.com)
7 *
8 * Derived from cbc.c
9 * - Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au>
David Howells91652be2006-12-16 12:09:02 +110010 */
11
Salvatore Mesoraca6650c4d2018-04-09 15:54:47 +020012#include <crypto/algapi.h>
Ard Biesheuvel0eb76ba2020-12-11 13:27:15 +010013#include <crypto/internal/cipher.h>
Herbert Xu043a4402016-11-22 20:08:27 +080014#include <crypto/internal/skcipher.h>
David Howells91652be2006-12-16 12:09:02 +110015#include <linux/err.h>
16#include <linux/init.h>
17#include <linux/kernel.h>
18#include <linux/module.h>
David Howells91652be2006-12-16 12:09:02 +110019
Herbert Xu043a4402016-11-22 20:08:27 +080020static int crypto_pcbc_encrypt_segment(struct skcipher_request *req,
21 struct skcipher_walk *walk,
Herbert Xud0b90072007-11-20 17:49:49 +080022 struct crypto_cipher *tfm)
David Howells91652be2006-12-16 12:09:02 +110023{
David Howells91652be2006-12-16 12:09:02 +110024 int bsize = crypto_cipher_blocksize(tfm);
25 unsigned int nbytes = walk->nbytes;
26 u8 *src = walk->src.virt.addr;
27 u8 *dst = walk->dst.virt.addr;
Eric Biggers251b7ae2019-01-03 20:16:13 -080028 u8 * const iv = walk->iv;
David Howells91652be2006-12-16 12:09:02 +110029
30 do {
Herbert Xud0b90072007-11-20 17:49:49 +080031 crypto_xor(iv, src, bsize);
Herbert Xu043a4402016-11-22 20:08:27 +080032 crypto_cipher_encrypt_one(tfm, dst, iv);
Ard Biesheuvel45fe93d2017-07-24 11:28:04 +010033 crypto_xor_cpy(iv, dst, src, bsize);
David Howells91652be2006-12-16 12:09:02 +110034
35 src += bsize;
36 dst += bsize;
37 } while ((nbytes -= bsize) >= bsize);
38
39 return nbytes;
40}
41
Herbert Xu043a4402016-11-22 20:08:27 +080042static int crypto_pcbc_encrypt_inplace(struct skcipher_request *req,
43 struct skcipher_walk *walk,
Herbert Xud0b90072007-11-20 17:49:49 +080044 struct crypto_cipher *tfm)
David Howells91652be2006-12-16 12:09:02 +110045{
David Howells91652be2006-12-16 12:09:02 +110046 int bsize = crypto_cipher_blocksize(tfm);
47 unsigned int nbytes = walk->nbytes;
48 u8 *src = walk->src.virt.addr;
Eric Biggers251b7ae2019-01-03 20:16:13 -080049 u8 * const iv = walk->iv;
Salvatore Mesoraca6650c4d2018-04-09 15:54:47 +020050 u8 tmpbuf[MAX_CIPHER_BLOCKSIZE];
David Howells91652be2006-12-16 12:09:02 +110051
52 do {
53 memcpy(tmpbuf, src, bsize);
Herbert Xud0b90072007-11-20 17:49:49 +080054 crypto_xor(iv, src, bsize);
Herbert Xu043a4402016-11-22 20:08:27 +080055 crypto_cipher_encrypt_one(tfm, src, iv);
Ard Biesheuvel45fe93d2017-07-24 11:28:04 +010056 crypto_xor_cpy(iv, tmpbuf, src, bsize);
David Howells91652be2006-12-16 12:09:02 +110057
58 src += bsize;
59 } while ((nbytes -= bsize) >= bsize);
60
David Howells91652be2006-12-16 12:09:02 +110061 return nbytes;
62}
63
Herbert Xu043a4402016-11-22 20:08:27 +080064static int crypto_pcbc_encrypt(struct skcipher_request *req)
David Howells91652be2006-12-16 12:09:02 +110065{
Herbert Xu043a4402016-11-22 20:08:27 +080066 struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
Eric Biggers0be487b2019-01-03 20:16:22 -080067 struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
Herbert Xu043a4402016-11-22 20:08:27 +080068 struct skcipher_walk walk;
69 unsigned int nbytes;
David Howells91652be2006-12-16 12:09:02 +110070 int err;
71
Herbert Xu043a4402016-11-22 20:08:27 +080072 err = skcipher_walk_virt(&walk, req, false);
David Howells91652be2006-12-16 12:09:02 +110073
74 while ((nbytes = walk.nbytes)) {
75 if (walk.src.virt.addr == walk.dst.virt.addr)
Herbert Xu043a4402016-11-22 20:08:27 +080076 nbytes = crypto_pcbc_encrypt_inplace(req, &walk,
Eric Biggers0be487b2019-01-03 20:16:22 -080077 cipher);
David Howells91652be2006-12-16 12:09:02 +110078 else
Herbert Xu043a4402016-11-22 20:08:27 +080079 nbytes = crypto_pcbc_encrypt_segment(req, &walk,
Eric Biggers0be487b2019-01-03 20:16:22 -080080 cipher);
Herbert Xu043a4402016-11-22 20:08:27 +080081 err = skcipher_walk_done(&walk, nbytes);
David Howells91652be2006-12-16 12:09:02 +110082 }
83
84 return err;
85}
86
Herbert Xu043a4402016-11-22 20:08:27 +080087static int crypto_pcbc_decrypt_segment(struct skcipher_request *req,
88 struct skcipher_walk *walk,
Herbert Xud0b90072007-11-20 17:49:49 +080089 struct crypto_cipher *tfm)
David Howells91652be2006-12-16 12:09:02 +110090{
David Howells91652be2006-12-16 12:09:02 +110091 int bsize = crypto_cipher_blocksize(tfm);
92 unsigned int nbytes = walk->nbytes;
93 u8 *src = walk->src.virt.addr;
94 u8 *dst = walk->dst.virt.addr;
Eric Biggers251b7ae2019-01-03 20:16:13 -080095 u8 * const iv = walk->iv;
David Howells91652be2006-12-16 12:09:02 +110096
97 do {
Herbert Xu043a4402016-11-22 20:08:27 +080098 crypto_cipher_decrypt_one(tfm, dst, src);
Herbert Xud0b90072007-11-20 17:49:49 +080099 crypto_xor(dst, iv, bsize);
Ard Biesheuvel45fe93d2017-07-24 11:28:04 +0100100 crypto_xor_cpy(iv, dst, src, bsize);
David Howells91652be2006-12-16 12:09:02 +1100101
102 src += bsize;
103 dst += bsize;
104 } while ((nbytes -= bsize) >= bsize);
105
David Howells91652be2006-12-16 12:09:02 +1100106 return nbytes;
107}
108
Herbert Xu043a4402016-11-22 20:08:27 +0800109static int crypto_pcbc_decrypt_inplace(struct skcipher_request *req,
110 struct skcipher_walk *walk,
Herbert Xud0b90072007-11-20 17:49:49 +0800111 struct crypto_cipher *tfm)
David Howells91652be2006-12-16 12:09:02 +1100112{
David Howells91652be2006-12-16 12:09:02 +1100113 int bsize = crypto_cipher_blocksize(tfm);
114 unsigned int nbytes = walk->nbytes;
115 u8 *src = walk->src.virt.addr;
Eric Biggers251b7ae2019-01-03 20:16:13 -0800116 u8 * const iv = walk->iv;
Salvatore Mesoraca6650c4d2018-04-09 15:54:47 +0200117 u8 tmpbuf[MAX_CIPHER_BLOCKSIZE] __aligned(__alignof__(u32));
David Howells91652be2006-12-16 12:09:02 +1100118
119 do {
120 memcpy(tmpbuf, src, bsize);
Herbert Xu043a4402016-11-22 20:08:27 +0800121 crypto_cipher_decrypt_one(tfm, src, src);
Herbert Xud0b90072007-11-20 17:49:49 +0800122 crypto_xor(src, iv, bsize);
Ard Biesheuvel45fe93d2017-07-24 11:28:04 +0100123 crypto_xor_cpy(iv, src, tmpbuf, bsize);
David Howells91652be2006-12-16 12:09:02 +1100124
125 src += bsize;
126 } while ((nbytes -= bsize) >= bsize);
127
David Howells91652be2006-12-16 12:09:02 +1100128 return nbytes;
129}
130
Herbert Xu043a4402016-11-22 20:08:27 +0800131static int crypto_pcbc_decrypt(struct skcipher_request *req)
David Howells91652be2006-12-16 12:09:02 +1100132{
Herbert Xu043a4402016-11-22 20:08:27 +0800133 struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
Eric Biggers0be487b2019-01-03 20:16:22 -0800134 struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
Herbert Xu043a4402016-11-22 20:08:27 +0800135 struct skcipher_walk walk;
136 unsigned int nbytes;
David Howells91652be2006-12-16 12:09:02 +1100137 int err;
138
Herbert Xu043a4402016-11-22 20:08:27 +0800139 err = skcipher_walk_virt(&walk, req, false);
David Howells91652be2006-12-16 12:09:02 +1100140
141 while ((nbytes = walk.nbytes)) {
142 if (walk.src.virt.addr == walk.dst.virt.addr)
Herbert Xu043a4402016-11-22 20:08:27 +0800143 nbytes = crypto_pcbc_decrypt_inplace(req, &walk,
Eric Biggers0be487b2019-01-03 20:16:22 -0800144 cipher);
David Howells91652be2006-12-16 12:09:02 +1100145 else
Herbert Xu043a4402016-11-22 20:08:27 +0800146 nbytes = crypto_pcbc_decrypt_segment(req, &walk,
Eric Biggers0be487b2019-01-03 20:16:22 -0800147 cipher);
Herbert Xu043a4402016-11-22 20:08:27 +0800148 err = skcipher_walk_done(&walk, nbytes);
David Howells91652be2006-12-16 12:09:02 +1100149 }
150
151 return err;
152}
153
Herbert Xu043a4402016-11-22 20:08:27 +0800154static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb)
155{
156 struct skcipher_instance *inst;
Herbert Xuebc610e2007-01-01 18:37:02 +1100157 int err;
David Howells91652be2006-12-16 12:09:02 +1100158
Herbert Xub3c16bf2019-12-20 13:29:40 +0800159 inst = skcipher_alloc_instance_simple(tmpl, tb);
Eric Biggers0be487b2019-01-03 20:16:22 -0800160 if (IS_ERR(inst))
161 return PTR_ERR(inst);
Herbert Xuebc610e2007-01-01 18:37:02 +1100162
Herbert Xu043a4402016-11-22 20:08:27 +0800163 inst->alg.encrypt = crypto_pcbc_encrypt;
164 inst->alg.decrypt = crypto_pcbc_decrypt;
David Howells91652be2006-12-16 12:09:02 +1100165
Herbert Xu043a4402016-11-22 20:08:27 +0800166 err = skcipher_register_instance(tmpl, inst);
167 if (err)
Eric Biggers0be487b2019-01-03 20:16:22 -0800168 inst->free(inst);
Herbert Xub3c16bf2019-12-20 13:29:40 +0800169
Herbert Xu043a4402016-11-22 20:08:27 +0800170 return err;
David Howells91652be2006-12-16 12:09:02 +1100171}
172
173static struct crypto_template crypto_pcbc_tmpl = {
174 .name = "pcbc",
Herbert Xu043a4402016-11-22 20:08:27 +0800175 .create = crypto_pcbc_create,
David Howells91652be2006-12-16 12:09:02 +1100176 .module = THIS_MODULE,
177};
178
179static int __init crypto_pcbc_module_init(void)
180{
181 return crypto_register_template(&crypto_pcbc_tmpl);
182}
183
184static void __exit crypto_pcbc_module_exit(void)
185{
186 crypto_unregister_template(&crypto_pcbc_tmpl);
187}
188
Eric Biggersc4741b22019-04-11 21:57:42 -0700189subsys_initcall(crypto_pcbc_module_init);
David Howells91652be2006-12-16 12:09:02 +1100190module_exit(crypto_pcbc_module_exit);
191
192MODULE_LICENSE("GPL");
Eric Biggers0be487b2019-01-03 20:16:22 -0800193MODULE_DESCRIPTION("PCBC block cipher mode of operation");
Kees Cook4943ba12014-11-24 16:32:38 -0800194MODULE_ALIAS_CRYPTO("pcbc");
Ard Biesheuvel0eb76ba2020-12-11 13:27:15 +0100195MODULE_IMPORT_NS(CRYPTO_INTERNAL);