Blame view

crypto/cbc.c 5.18 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
2
3
4
  /*
   * CBC: Cipher Block Chaining mode
   *
cc868d82a   Herbert Xu   crypto: cbc - Exp...
5
   * Copyright (c) 2006-2016 Herbert Xu <herbert@gondor.apana.org.au>
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
6
   */
e6c2e65c7   Marcelo Cerri   crypto: cbc - Pro...
7
  #include <crypto/algapi.h>
0eb76ba29   Ard Biesheuvel   crypto: remove ci...
8
  #include <crypto/internal/cipher.h>
79c65d179   Herbert Xu   crypto: cbc - Con...
9
  #include <crypto/internal/skcipher.h>
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
10
11
12
  #include <linux/err.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
50b6544e1   Herbert Xu   [CRYPTO] cbc: Req...
13
  #include <linux/log2.h>
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
14
  #include <linux/module.h>
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
15

5f254dd44   Herbert Xu   crypto: cbc - Rem...
16
17
  static int crypto_cbc_encrypt_segment(struct skcipher_walk *walk,
  				      struct crypto_skcipher *skcipher)
79c65d179   Herbert Xu   crypto: cbc - Con...
18
  {
5f254dd44   Herbert Xu   crypto: cbc - Rem...
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
  	unsigned int bsize = crypto_skcipher_blocksize(skcipher);
  	void (*fn)(struct crypto_tfm *, u8 *, const u8 *);
  	unsigned int nbytes = walk->nbytes;
  	u8 *src = walk->src.virt.addr;
  	u8 *dst = walk->dst.virt.addr;
  	struct crypto_cipher *cipher;
  	struct crypto_tfm *tfm;
  	u8 *iv = walk->iv;
  
  	cipher = skcipher_cipher_simple(skcipher);
  	tfm = crypto_cipher_tfm(cipher);
  	fn = crypto_cipher_alg(cipher)->cia_encrypt;
  
  	do {
  		crypto_xor(iv, src, bsize);
  		fn(tfm, dst, iv);
  		memcpy(iv, dst, bsize);
  
  		src += bsize;
  		dst += bsize;
  	} while ((nbytes -= bsize) >= bsize);
  
  	return nbytes;
  }
  
  static int crypto_cbc_encrypt_inplace(struct skcipher_walk *walk,
  				      struct crypto_skcipher *skcipher)
  {
  	unsigned int bsize = crypto_skcipher_blocksize(skcipher);
  	void (*fn)(struct crypto_tfm *, u8 *, const u8 *);
  	unsigned int nbytes = walk->nbytes;
  	u8 *src = walk->src.virt.addr;
  	struct crypto_cipher *cipher;
  	struct crypto_tfm *tfm;
  	u8 *iv = walk->iv;
  
  	cipher = skcipher_cipher_simple(skcipher);
  	tfm = crypto_cipher_tfm(cipher);
  	fn = crypto_cipher_alg(cipher)->cia_encrypt;
  
  	do {
  		crypto_xor(src, iv, bsize);
  		fn(tfm, src, src);
  		iv = src;
  
  		src += bsize;
  	} while ((nbytes -= bsize) >= bsize);
  
  	memcpy(walk->iv, iv, bsize);
  
  	return nbytes;
79c65d179   Herbert Xu   crypto: cbc - Con...
70
71
72
  }
  
  static int crypto_cbc_encrypt(struct skcipher_request *req)
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
73
  {
5f254dd44   Herbert Xu   crypto: cbc - Rem...
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
  	struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req);
  	struct skcipher_walk walk;
  	int err;
  
  	err = skcipher_walk_virt(&walk, req, false);
  
  	while (walk.nbytes) {
  		if (walk.src.virt.addr == walk.dst.virt.addr)
  			err = crypto_cbc_encrypt_inplace(&walk, skcipher);
  		else
  			err = crypto_cbc_encrypt_segment(&walk, skcipher);
  		err = skcipher_walk_done(&walk, err);
  	}
  
  	return err;
  }
  
  static int crypto_cbc_decrypt_segment(struct skcipher_walk *walk,
  				      struct crypto_skcipher *skcipher)
  {
  	unsigned int bsize = crypto_skcipher_blocksize(skcipher);
  	void (*fn)(struct crypto_tfm *, u8 *, const u8 *);
  	unsigned int nbytes = walk->nbytes;
  	u8 *src = walk->src.virt.addr;
  	u8 *dst = walk->dst.virt.addr;
  	struct crypto_cipher *cipher;
  	struct crypto_tfm *tfm;
  	u8 *iv = walk->iv;
  
  	cipher = skcipher_cipher_simple(skcipher);
  	tfm = crypto_cipher_tfm(cipher);
  	fn = crypto_cipher_alg(cipher)->cia_decrypt;
  
  	do {
  		fn(tfm, dst, src);
  		crypto_xor(dst, iv, bsize);
  		iv = src;
  
  		src += bsize;
  		dst += bsize;
  	} while ((nbytes -= bsize) >= bsize);
  
  	memcpy(walk->iv, iv, bsize);
  
  	return nbytes;
79c65d179   Herbert Xu   crypto: cbc - Con...
119
  }
5f254dd44   Herbert Xu   crypto: cbc - Rem...
120
121
  static int crypto_cbc_decrypt_inplace(struct skcipher_walk *walk,
  				      struct crypto_skcipher *skcipher)
79c65d179   Herbert Xu   crypto: cbc - Con...
122
  {
5f254dd44   Herbert Xu   crypto: cbc - Rem...
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  	unsigned int bsize = crypto_skcipher_blocksize(skcipher);
  	void (*fn)(struct crypto_tfm *, u8 *, const u8 *);
  	unsigned int nbytes = walk->nbytes;
  	u8 *src = walk->src.virt.addr;
  	u8 last_iv[MAX_CIPHER_BLOCKSIZE];
  	struct crypto_cipher *cipher;
  	struct crypto_tfm *tfm;
  
  	cipher = skcipher_cipher_simple(skcipher);
  	tfm = crypto_cipher_tfm(cipher);
  	fn = crypto_cipher_alg(cipher)->cia_decrypt;
  
  	/* Start of the last block. */
  	src += nbytes - (nbytes & (bsize - 1)) - bsize;
  	memcpy(last_iv, src, bsize);
  
  	for (;;) {
  		fn(tfm, src, src);
  		if ((nbytes -= bsize) < bsize)
  			break;
  		crypto_xor(src, src - bsize, bsize);
  		src -= bsize;
  	}
  
  	crypto_xor(src, walk->iv, bsize);
  	memcpy(walk->iv, last_iv, bsize);
  
  	return nbytes;
79c65d179   Herbert Xu   crypto: cbc - Con...
151
152
153
154
  }
  
  static int crypto_cbc_decrypt(struct skcipher_request *req)
  {
5f254dd44   Herbert Xu   crypto: cbc - Rem...
155
  	struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req);
79c65d179   Herbert Xu   crypto: cbc - Con...
156
  	struct skcipher_walk walk;
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
157
  	int err;
79c65d179   Herbert Xu   crypto: cbc - Con...
158
  	err = skcipher_walk_virt(&walk, req, false);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
159

79c65d179   Herbert Xu   crypto: cbc - Con...
160
  	while (walk.nbytes) {
5f254dd44   Herbert Xu   crypto: cbc - Rem...
161
162
163
164
  		if (walk.src.virt.addr == walk.dst.virt.addr)
  			err = crypto_cbc_decrypt_inplace(&walk, skcipher);
  		else
  			err = crypto_cbc_decrypt_segment(&walk, skcipher);
79c65d179   Herbert Xu   crypto: cbc - Con...
165
  		err = skcipher_walk_done(&walk, err);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
166
167
168
169
  	}
  
  	return err;
  }
79c65d179   Herbert Xu   crypto: cbc - Con...
170
171
172
  static int crypto_cbc_create(struct crypto_template *tmpl, struct rtattr **tb)
  {
  	struct skcipher_instance *inst;
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
173
  	struct crypto_alg *alg;
ebc610e5b   Herbert Xu   [CRYPTO] template...
174
  	int err;
b3c16bfc6   Herbert Xu   crypto: skcipher ...
175
  	inst = skcipher_alloc_instance_simple(tmpl, tb);
a5a84a9db   Eric Biggers   crypto: cbc - con...
176
177
  	if (IS_ERR(inst))
  		return PTR_ERR(inst);
50b6544e1   Herbert Xu   [CRYPTO] cbc: Req...
178

b3c16bfc6   Herbert Xu   crypto: skcipher ...
179
  	alg = skcipher_ialg_simple(inst);
79c65d179   Herbert Xu   crypto: cbc - Con...
180
181
  	err = -EINVAL;
  	if (!is_power_of_2(alg->cra_blocksize))
a5a84a9db   Eric Biggers   crypto: cbc - con...
182
  		goto out_free_inst;
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
183

79c65d179   Herbert Xu   crypto: cbc - Con...
184
185
  	inst->alg.encrypt = crypto_cbc_encrypt;
  	inst->alg.decrypt = crypto_cbc_decrypt;
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
186

79c65d179   Herbert Xu   crypto: cbc - Con...
187
  	err = skcipher_register_instance(tmpl, inst);
b3c16bfc6   Herbert Xu   crypto: skcipher ...
188
  	if (err) {
a5a84a9db   Eric Biggers   crypto: cbc - con...
189
  out_free_inst:
b3c16bfc6   Herbert Xu   crypto: skcipher ...
190
191
  		inst->free(inst);
  	}
a5a84a9db   Eric Biggers   crypto: cbc - con...
192
  	return err;
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
193
194
195
196
  }
  
  static struct crypto_template crypto_cbc_tmpl = {
  	.name = "cbc",
79c65d179   Herbert Xu   crypto: cbc - Con...
197
  	.create = crypto_cbc_create,
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
198
199
200
201
202
203
204
205
206
207
208
209
  	.module = THIS_MODULE,
  };
  
  static int __init crypto_cbc_module_init(void)
  {
  	return crypto_register_template(&crypto_cbc_tmpl);
  }
  
  static void __exit crypto_cbc_module_exit(void)
  {
  	crypto_unregister_template(&crypto_cbc_tmpl);
  }
c4741b230   Eric Biggers   crypto: run initc...
210
  subsys_initcall(crypto_cbc_module_init);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
211
212
213
  module_exit(crypto_cbc_module_exit);
  
  MODULE_LICENSE("GPL");
a5a84a9db   Eric Biggers   crypto: cbc - con...
214
  MODULE_DESCRIPTION("CBC block cipher mode of operation");
4943ba16b   Kees Cook   crypto: include c...
215
  MODULE_ALIAS_CRYPTO("cbc");