blob: 862cdb8d8b6cf262cef2b1405fe4a1cebecbe684 [file] [log] [blame]
David Brazdil0f672f62019-12-10 10:32:29 +00001// SPDX-License-Identifier: GPL-2.0-or-later
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002/*
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>
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000010 */
11
12#include <crypto/algapi.h>
13#include <crypto/internal/skcipher.h>
14#include <linux/err.h>
15#include <linux/init.h>
16#include <linux/kernel.h>
17#include <linux/module.h>
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000018
19static int crypto_pcbc_encrypt_segment(struct skcipher_request *req,
20 struct skcipher_walk *walk,
21 struct crypto_cipher *tfm)
22{
23 int bsize = crypto_cipher_blocksize(tfm);
24 unsigned int nbytes = walk->nbytes;
25 u8 *src = walk->src.virt.addr;
26 u8 *dst = walk->dst.virt.addr;
David Brazdil0f672f62019-12-10 10:32:29 +000027 u8 * const iv = walk->iv;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000028
29 do {
30 crypto_xor(iv, src, bsize);
31 crypto_cipher_encrypt_one(tfm, dst, iv);
32 crypto_xor_cpy(iv, dst, src, bsize);
33
34 src += bsize;
35 dst += bsize;
36 } while ((nbytes -= bsize) >= bsize);
37
38 return nbytes;
39}
40
41static int crypto_pcbc_encrypt_inplace(struct skcipher_request *req,
42 struct skcipher_walk *walk,
43 struct crypto_cipher *tfm)
44{
45 int bsize = crypto_cipher_blocksize(tfm);
46 unsigned int nbytes = walk->nbytes;
47 u8 *src = walk->src.virt.addr;
David Brazdil0f672f62019-12-10 10:32:29 +000048 u8 * const iv = walk->iv;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000049 u8 tmpbuf[MAX_CIPHER_BLOCKSIZE];
50
51 do {
52 memcpy(tmpbuf, src, bsize);
53 crypto_xor(iv, src, bsize);
54 crypto_cipher_encrypt_one(tfm, src, iv);
55 crypto_xor_cpy(iv, tmpbuf, src, bsize);
56
57 src += bsize;
58 } while ((nbytes -= bsize) >= bsize);
59
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000060 return nbytes;
61}
62
63static int crypto_pcbc_encrypt(struct skcipher_request *req)
64{
65 struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
David Brazdil0f672f62019-12-10 10:32:29 +000066 struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000067 struct skcipher_walk walk;
68 unsigned int nbytes;
69 int err;
70
71 err = skcipher_walk_virt(&walk, req, false);
72
73 while ((nbytes = walk.nbytes)) {
74 if (walk.src.virt.addr == walk.dst.virt.addr)
75 nbytes = crypto_pcbc_encrypt_inplace(req, &walk,
David Brazdil0f672f62019-12-10 10:32:29 +000076 cipher);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000077 else
78 nbytes = crypto_pcbc_encrypt_segment(req, &walk,
David Brazdil0f672f62019-12-10 10:32:29 +000079 cipher);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000080 err = skcipher_walk_done(&walk, nbytes);
81 }
82
83 return err;
84}
85
86static int crypto_pcbc_decrypt_segment(struct skcipher_request *req,
87 struct skcipher_walk *walk,
88 struct crypto_cipher *tfm)
89{
90 int bsize = crypto_cipher_blocksize(tfm);
91 unsigned int nbytes = walk->nbytes;
92 u8 *src = walk->src.virt.addr;
93 u8 *dst = walk->dst.virt.addr;
David Brazdil0f672f62019-12-10 10:32:29 +000094 u8 * const iv = walk->iv;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000095
96 do {
97 crypto_cipher_decrypt_one(tfm, dst, src);
98 crypto_xor(dst, iv, bsize);
99 crypto_xor_cpy(iv, dst, src, bsize);
100
101 src += bsize;
102 dst += bsize;
103 } while ((nbytes -= bsize) >= bsize);
104
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000105 return nbytes;
106}
107
108static int crypto_pcbc_decrypt_inplace(struct skcipher_request *req,
109 struct skcipher_walk *walk,
110 struct crypto_cipher *tfm)
111{
112 int bsize = crypto_cipher_blocksize(tfm);
113 unsigned int nbytes = walk->nbytes;
114 u8 *src = walk->src.virt.addr;
David Brazdil0f672f62019-12-10 10:32:29 +0000115 u8 * const iv = walk->iv;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000116 u8 tmpbuf[MAX_CIPHER_BLOCKSIZE] __aligned(__alignof__(u32));
117
118 do {
119 memcpy(tmpbuf, src, bsize);
120 crypto_cipher_decrypt_one(tfm, src, src);
121 crypto_xor(src, iv, bsize);
122 crypto_xor_cpy(iv, src, tmpbuf, bsize);
123
124 src += bsize;
125 } while ((nbytes -= bsize) >= bsize);
126
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000127 return nbytes;
128}
129
130static int crypto_pcbc_decrypt(struct skcipher_request *req)
131{
132 struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
David Brazdil0f672f62019-12-10 10:32:29 +0000133 struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000134 struct skcipher_walk walk;
135 unsigned int nbytes;
136 int err;
137
138 err = skcipher_walk_virt(&walk, req, false);
139
140 while ((nbytes = walk.nbytes)) {
141 if (walk.src.virt.addr == walk.dst.virt.addr)
142 nbytes = crypto_pcbc_decrypt_inplace(req, &walk,
David Brazdil0f672f62019-12-10 10:32:29 +0000143 cipher);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000144 else
145 nbytes = crypto_pcbc_decrypt_segment(req, &walk,
David Brazdil0f672f62019-12-10 10:32:29 +0000146 cipher);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000147 err = skcipher_walk_done(&walk, nbytes);
148 }
149
150 return err;
151}
152
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000153static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb)
154{
155 struct skcipher_instance *inst;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000156 struct crypto_alg *alg;
157 int err;
158
David Brazdil0f672f62019-12-10 10:32:29 +0000159 inst = skcipher_alloc_instance_simple(tmpl, tb, &alg);
160 if (IS_ERR(inst))
161 return PTR_ERR(inst);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000162
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000163 inst->alg.encrypt = crypto_pcbc_encrypt;
164 inst->alg.decrypt = crypto_pcbc_decrypt;
165
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000166 err = skcipher_register_instance(tmpl, inst);
167 if (err)
David Brazdil0f672f62019-12-10 10:32:29 +0000168 inst->free(inst);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000169 crypto_mod_put(alg);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000170 return err;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000171}
172
173static struct crypto_template crypto_pcbc_tmpl = {
174 .name = "pcbc",
175 .create = crypto_pcbc_create,
176 .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
David Brazdil0f672f62019-12-10 10:32:29 +0000189subsys_initcall(crypto_pcbc_module_init);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000190module_exit(crypto_pcbc_module_exit);
191
192MODULE_LICENSE("GPL");
David Brazdil0f672f62019-12-10 10:32:29 +0000193MODULE_DESCRIPTION("PCBC block cipher mode of operation");
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000194MODULE_ALIAS_CRYPTO("pcbc");