]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
91652be5 DH |
2 | /* |
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> | |
91652be5 DH |
10 | */ |
11 | ||
6650c4de | 12 | #include <crypto/algapi.h> |
043a4400 | 13 | #include <crypto/internal/skcipher.h> |
91652be5 DH |
14 | #include <linux/err.h> |
15 | #include <linux/init.h> | |
16 | #include <linux/kernel.h> | |
17 | #include <linux/module.h> | |
91652be5 | 18 | |
043a4400 HX |
19 | static int crypto_pcbc_encrypt_segment(struct skcipher_request *req, |
20 | struct skcipher_walk *walk, | |
d0b9007a | 21 | struct crypto_cipher *tfm) |
91652be5 | 22 | { |
91652be5 DH |
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; | |
251b7aea | 27 | u8 * const iv = walk->iv; |
91652be5 DH |
28 | |
29 | do { | |
d0b9007a | 30 | crypto_xor(iv, src, bsize); |
043a4400 | 31 | crypto_cipher_encrypt_one(tfm, dst, iv); |
45fe93df | 32 | crypto_xor_cpy(iv, dst, src, bsize); |
91652be5 DH |
33 | |
34 | src += bsize; | |
35 | dst += bsize; | |
36 | } while ((nbytes -= bsize) >= bsize); | |
37 | ||
38 | return nbytes; | |
39 | } | |
40 | ||
043a4400 HX |
41 | static int crypto_pcbc_encrypt_inplace(struct skcipher_request *req, |
42 | struct skcipher_walk *walk, | |
d0b9007a | 43 | struct crypto_cipher *tfm) |
91652be5 | 44 | { |
91652be5 DH |
45 | int bsize = crypto_cipher_blocksize(tfm); |
46 | unsigned int nbytes = walk->nbytes; | |
47 | u8 *src = walk->src.virt.addr; | |
251b7aea | 48 | u8 * const iv = walk->iv; |
6650c4de | 49 | u8 tmpbuf[MAX_CIPHER_BLOCKSIZE]; |
91652be5 DH |
50 | |
51 | do { | |
52 | memcpy(tmpbuf, src, bsize); | |
d0b9007a | 53 | crypto_xor(iv, src, bsize); |
043a4400 | 54 | crypto_cipher_encrypt_one(tfm, src, iv); |
45fe93df | 55 | crypto_xor_cpy(iv, tmpbuf, src, bsize); |
91652be5 DH |
56 | |
57 | src += bsize; | |
58 | } while ((nbytes -= bsize) >= bsize); | |
59 | ||
91652be5 DH |
60 | return nbytes; |
61 | } | |
62 | ||
043a4400 | 63 | static int crypto_pcbc_encrypt(struct skcipher_request *req) |
91652be5 | 64 | { |
043a4400 | 65 | struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); |
0be487ba | 66 | struct crypto_cipher *cipher = skcipher_cipher_simple(tfm); |
043a4400 HX |
67 | struct skcipher_walk walk; |
68 | unsigned int nbytes; | |
91652be5 DH |
69 | int err; |
70 | ||
043a4400 | 71 | err = skcipher_walk_virt(&walk, req, false); |
91652be5 DH |
72 | |
73 | while ((nbytes = walk.nbytes)) { | |
74 | if (walk.src.virt.addr == walk.dst.virt.addr) | |
043a4400 | 75 | nbytes = crypto_pcbc_encrypt_inplace(req, &walk, |
0be487ba | 76 | cipher); |
91652be5 | 77 | else |
043a4400 | 78 | nbytes = crypto_pcbc_encrypt_segment(req, &walk, |
0be487ba | 79 | cipher); |
043a4400 | 80 | err = skcipher_walk_done(&walk, nbytes); |
91652be5 DH |
81 | } |
82 | ||
83 | return err; | |
84 | } | |
85 | ||
043a4400 HX |
86 | static int crypto_pcbc_decrypt_segment(struct skcipher_request *req, |
87 | struct skcipher_walk *walk, | |
d0b9007a | 88 | struct crypto_cipher *tfm) |
91652be5 | 89 | { |
91652be5 DH |
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; | |
251b7aea | 94 | u8 * const iv = walk->iv; |
91652be5 DH |
95 | |
96 | do { | |
043a4400 | 97 | crypto_cipher_decrypt_one(tfm, dst, src); |
d0b9007a | 98 | crypto_xor(dst, iv, bsize); |
45fe93df | 99 | crypto_xor_cpy(iv, dst, src, bsize); |
91652be5 DH |
100 | |
101 | src += bsize; | |
102 | dst += bsize; | |
103 | } while ((nbytes -= bsize) >= bsize); | |
104 | ||
91652be5 DH |
105 | return nbytes; |
106 | } | |
107 | ||
043a4400 HX |
108 | static int crypto_pcbc_decrypt_inplace(struct skcipher_request *req, |
109 | struct skcipher_walk *walk, | |
d0b9007a | 110 | struct crypto_cipher *tfm) |
91652be5 | 111 | { |
91652be5 DH |
112 | int bsize = crypto_cipher_blocksize(tfm); |
113 | unsigned int nbytes = walk->nbytes; | |
114 | u8 *src = walk->src.virt.addr; | |
251b7aea | 115 | u8 * const iv = walk->iv; |
6650c4de | 116 | u8 tmpbuf[MAX_CIPHER_BLOCKSIZE] __aligned(__alignof__(u32)); |
91652be5 DH |
117 | |
118 | do { | |
119 | memcpy(tmpbuf, src, bsize); | |
043a4400 | 120 | crypto_cipher_decrypt_one(tfm, src, src); |
d0b9007a | 121 | crypto_xor(src, iv, bsize); |
45fe93df | 122 | crypto_xor_cpy(iv, src, tmpbuf, bsize); |
91652be5 DH |
123 | |
124 | src += bsize; | |
125 | } while ((nbytes -= bsize) >= bsize); | |
126 | ||
91652be5 DH |
127 | return nbytes; |
128 | } | |
129 | ||
043a4400 | 130 | static int crypto_pcbc_decrypt(struct skcipher_request *req) |
91652be5 | 131 | { |
043a4400 | 132 | struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); |
0be487ba | 133 | struct crypto_cipher *cipher = skcipher_cipher_simple(tfm); |
043a4400 HX |
134 | struct skcipher_walk walk; |
135 | unsigned int nbytes; | |
91652be5 DH |
136 | int err; |
137 | ||
043a4400 | 138 | err = skcipher_walk_virt(&walk, req, false); |
91652be5 DH |
139 | |
140 | while ((nbytes = walk.nbytes)) { | |
141 | if (walk.src.virt.addr == walk.dst.virt.addr) | |
043a4400 | 142 | nbytes = crypto_pcbc_decrypt_inplace(req, &walk, |
0be487ba | 143 | cipher); |
91652be5 | 144 | else |
043a4400 | 145 | nbytes = crypto_pcbc_decrypt_segment(req, &walk, |
0be487ba | 146 | cipher); |
043a4400 | 147 | err = skcipher_walk_done(&walk, nbytes); |
91652be5 DH |
148 | } |
149 | ||
150 | return err; | |
151 | } | |
152 | ||
043a4400 | 153 | static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb) |
91652be5 | 154 | { |
043a4400 | 155 | struct skcipher_instance *inst; |
ebc610e5 HX |
156 | int err; |
157 | ||
b3c16bfc | 158 | inst = skcipher_alloc_instance_simple(tmpl, tb); |
0be487ba EB |
159 | if (IS_ERR(inst)) |
160 | return PTR_ERR(inst); | |
91652be5 | 161 | |
043a4400 HX |
162 | inst->alg.encrypt = crypto_pcbc_encrypt; |
163 | inst->alg.decrypt = crypto_pcbc_decrypt; | |
91652be5 | 164 | |
043a4400 HX |
165 | err = skcipher_register_instance(tmpl, inst); |
166 | if (err) | |
0be487ba | 167 | inst->free(inst); |
b3c16bfc | 168 | |
043a4400 | 169 | return err; |
91652be5 DH |
170 | } |
171 | ||
172 | static struct crypto_template crypto_pcbc_tmpl = { | |
173 | .name = "pcbc", | |
043a4400 | 174 | .create = crypto_pcbc_create, |
91652be5 DH |
175 | .module = THIS_MODULE, |
176 | }; | |
177 | ||
178 | static int __init crypto_pcbc_module_init(void) | |
179 | { | |
180 | return crypto_register_template(&crypto_pcbc_tmpl); | |
181 | } | |
182 | ||
183 | static void __exit crypto_pcbc_module_exit(void) | |
184 | { | |
185 | crypto_unregister_template(&crypto_pcbc_tmpl); | |
186 | } | |
187 | ||
c4741b23 | 188 | subsys_initcall(crypto_pcbc_module_init); |
91652be5 DH |
189 | module_exit(crypto_pcbc_module_exit); |
190 | ||
191 | MODULE_LICENSE("GPL"); | |
0be487ba | 192 | MODULE_DESCRIPTION("PCBC block cipher mode of operation"); |
4943ba16 | 193 | MODULE_ALIAS_CRYPTO("pcbc"); |