Blame view

crypto/pcbc.c 7.69 KB
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * PCBC: Propagating Cipher Block Chaining mode
   *
   * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
   *
   * Derived from cbc.c
   * - Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au>
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License as published by the Free
   * Software Foundation; either version 2 of the License, or (at your option)
   * any later version.
   *
   */
043a44001   Herbert Xu   crypto: pcbc - Co...
16
  #include <crypto/internal/skcipher.h>
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
17
18
19
20
  #include <linux/err.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
21
  #include <linux/slab.h>
d8c34b949   Gideon Israel Dsouza   crypto: Replaced ...
22
  #include <linux/compiler.h>
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
23
24
25
  
  struct crypto_pcbc_ctx {
  	struct crypto_cipher *child;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
26
  };
043a44001   Herbert Xu   crypto: pcbc - Co...
27
  static int crypto_pcbc_setkey(struct crypto_skcipher *parent, const u8 *key,
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
28
29
  			      unsigned int keylen)
  {
043a44001   Herbert Xu   crypto: pcbc - Co...
30
  	struct crypto_pcbc_ctx *ctx = crypto_skcipher_ctx(parent);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
31
32
33
34
  	struct crypto_cipher *child = ctx->child;
  	int err;
  
  	crypto_cipher_clear_flags(child, CRYPTO_TFM_REQ_MASK);
043a44001   Herbert Xu   crypto: pcbc - Co...
35
36
  	crypto_cipher_set_flags(child, crypto_skcipher_get_flags(parent) &
  				       CRYPTO_TFM_REQ_MASK);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
37
  	err = crypto_cipher_setkey(child, key, keylen);
043a44001   Herbert Xu   crypto: pcbc - Co...
38
39
  	crypto_skcipher_set_flags(parent, crypto_cipher_get_flags(child) &
  					  CRYPTO_TFM_RES_MASK);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
40
41
  	return err;
  }
043a44001   Herbert Xu   crypto: pcbc - Co...
42
43
  static int crypto_pcbc_encrypt_segment(struct skcipher_request *req,
  				       struct skcipher_walk *walk,
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
44
  				       struct crypto_cipher *tfm)
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
45
  {
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
46
47
48
49
50
51
52
  	int bsize = crypto_cipher_blocksize(tfm);
  	unsigned int nbytes = walk->nbytes;
  	u8 *src = walk->src.virt.addr;
  	u8 *dst = walk->dst.virt.addr;
  	u8 *iv = walk->iv;
  
  	do {
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
53
  		crypto_xor(iv, src, bsize);
043a44001   Herbert Xu   crypto: pcbc - Co...
54
  		crypto_cipher_encrypt_one(tfm, dst, iv);
45fe93dff   Ard Biesheuvel   crypto: algapi - ...
55
  		crypto_xor_cpy(iv, dst, src, bsize);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
56
57
58
59
60
61
62
  
  		src += bsize;
  		dst += bsize;
  	} while ((nbytes -= bsize) >= bsize);
  
  	return nbytes;
  }
043a44001   Herbert Xu   crypto: pcbc - Co...
63
64
  static int crypto_pcbc_encrypt_inplace(struct skcipher_request *req,
  				       struct skcipher_walk *walk,
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
65
  				       struct crypto_cipher *tfm)
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
66
  {
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
67
68
69
70
71
72
73
74
  	int bsize = crypto_cipher_blocksize(tfm);
  	unsigned int nbytes = walk->nbytes;
  	u8 *src = walk->src.virt.addr;
  	u8 *iv = walk->iv;
  	u8 tmpbuf[bsize];
  
  	do {
  		memcpy(tmpbuf, src, bsize);
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
75
  		crypto_xor(iv, src, bsize);
043a44001   Herbert Xu   crypto: pcbc - Co...
76
  		crypto_cipher_encrypt_one(tfm, src, iv);
45fe93dff   Ard Biesheuvel   crypto: algapi - ...
77
  		crypto_xor_cpy(iv, tmpbuf, src, bsize);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
78
79
80
81
82
83
84
85
  
  		src += bsize;
  	} while ((nbytes -= bsize) >= bsize);
  
  	memcpy(walk->iv, iv, bsize);
  
  	return nbytes;
  }
043a44001   Herbert Xu   crypto: pcbc - Co...
86
  static int crypto_pcbc_encrypt(struct skcipher_request *req)
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
87
  {
043a44001   Herbert Xu   crypto: pcbc - Co...
88
89
  	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
  	struct crypto_pcbc_ctx *ctx = crypto_skcipher_ctx(tfm);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
90
  	struct crypto_cipher *child = ctx->child;
043a44001   Herbert Xu   crypto: pcbc - Co...
91
92
  	struct skcipher_walk walk;
  	unsigned int nbytes;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
93
  	int err;
043a44001   Herbert Xu   crypto: pcbc - Co...
94
  	err = skcipher_walk_virt(&walk, req, false);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
95
96
97
  
  	while ((nbytes = walk.nbytes)) {
  		if (walk.src.virt.addr == walk.dst.virt.addr)
043a44001   Herbert Xu   crypto: pcbc - Co...
98
  			nbytes = crypto_pcbc_encrypt_inplace(req, &walk,
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
99
  							     child);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
100
  		else
043a44001   Herbert Xu   crypto: pcbc - Co...
101
  			nbytes = crypto_pcbc_encrypt_segment(req, &walk,
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
102
  							     child);
043a44001   Herbert Xu   crypto: pcbc - Co...
103
  		err = skcipher_walk_done(&walk, nbytes);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
104
105
106
107
  	}
  
  	return err;
  }
043a44001   Herbert Xu   crypto: pcbc - Co...
108
109
  static int crypto_pcbc_decrypt_segment(struct skcipher_request *req,
  				       struct skcipher_walk *walk,
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
110
  				       struct crypto_cipher *tfm)
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
111
  {
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
112
113
114
115
116
117
118
  	int bsize = crypto_cipher_blocksize(tfm);
  	unsigned int nbytes = walk->nbytes;
  	u8 *src = walk->src.virt.addr;
  	u8 *dst = walk->dst.virt.addr;
  	u8 *iv = walk->iv;
  
  	do {
043a44001   Herbert Xu   crypto: pcbc - Co...
119
  		crypto_cipher_decrypt_one(tfm, dst, src);
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
120
  		crypto_xor(dst, iv, bsize);
45fe93dff   Ard Biesheuvel   crypto: algapi - ...
121
  		crypto_xor_cpy(iv, dst, src, bsize);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
122
123
124
125
126
127
128
129
130
  
  		src += bsize;
  		dst += bsize;
  	} while ((nbytes -= bsize) >= bsize);
  
  	memcpy(walk->iv, iv, bsize);
  
  	return nbytes;
  }
043a44001   Herbert Xu   crypto: pcbc - Co...
131
132
  static int crypto_pcbc_decrypt_inplace(struct skcipher_request *req,
  				       struct skcipher_walk *walk,
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
133
  				       struct crypto_cipher *tfm)
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
134
  {
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
135
136
137
138
  	int bsize = crypto_cipher_blocksize(tfm);
  	unsigned int nbytes = walk->nbytes;
  	u8 *src = walk->src.virt.addr;
  	u8 *iv = walk->iv;
d8c34b949   Gideon Israel Dsouza   crypto: Replaced ...
139
  	u8 tmpbuf[bsize] __aligned(__alignof__(u32));
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
140
141
142
  
  	do {
  		memcpy(tmpbuf, src, bsize);
043a44001   Herbert Xu   crypto: pcbc - Co...
143
  		crypto_cipher_decrypt_one(tfm, src, src);
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
144
  		crypto_xor(src, iv, bsize);
45fe93dff   Ard Biesheuvel   crypto: algapi - ...
145
  		crypto_xor_cpy(iv, src, tmpbuf, bsize);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
146
147
148
149
150
151
152
153
  
  		src += bsize;
  	} while ((nbytes -= bsize) >= bsize);
  
  	memcpy(walk->iv, iv, bsize);
  
  	return nbytes;
  }
043a44001   Herbert Xu   crypto: pcbc - Co...
154
  static int crypto_pcbc_decrypt(struct skcipher_request *req)
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
155
  {
043a44001   Herbert Xu   crypto: pcbc - Co...
156
157
  	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
  	struct crypto_pcbc_ctx *ctx = crypto_skcipher_ctx(tfm);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
158
  	struct crypto_cipher *child = ctx->child;
043a44001   Herbert Xu   crypto: pcbc - Co...
159
160
  	struct skcipher_walk walk;
  	unsigned int nbytes;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
161
  	int err;
043a44001   Herbert Xu   crypto: pcbc - Co...
162
  	err = skcipher_walk_virt(&walk, req, false);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
163
164
165
  
  	while ((nbytes = walk.nbytes)) {
  		if (walk.src.virt.addr == walk.dst.virt.addr)
043a44001   Herbert Xu   crypto: pcbc - Co...
166
  			nbytes = crypto_pcbc_decrypt_inplace(req, &walk,
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
167
  							     child);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
168
  		else
043a44001   Herbert Xu   crypto: pcbc - Co...
169
  			nbytes = crypto_pcbc_decrypt_segment(req, &walk,
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
170
  							     child);
043a44001   Herbert Xu   crypto: pcbc - Co...
171
  		err = skcipher_walk_done(&walk, nbytes);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
172
173
174
175
  	}
  
  	return err;
  }
043a44001   Herbert Xu   crypto: pcbc - Co...
176
  static int crypto_pcbc_init_tfm(struct crypto_skcipher *tfm)
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
177
  {
043a44001   Herbert Xu   crypto: pcbc - Co...
178
179
180
  	struct skcipher_instance *inst = skcipher_alg_instance(tfm);
  	struct crypto_spawn *spawn = skcipher_instance_ctx(inst);
  	struct crypto_pcbc_ctx *ctx = crypto_skcipher_ctx(tfm);
2e306ee01   Herbert Xu   [CRYPTO] api: Add...
181
  	struct crypto_cipher *cipher;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
182

2e306ee01   Herbert Xu   [CRYPTO] api: Add...
183
184
185
  	cipher = crypto_spawn_cipher(spawn);
  	if (IS_ERR(cipher))
  		return PTR_ERR(cipher);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
186

2e306ee01   Herbert Xu   [CRYPTO] api: Add...
187
  	ctx->child = cipher;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
188
189
  	return 0;
  }
043a44001   Herbert Xu   crypto: pcbc - Co...
190
  static void crypto_pcbc_exit_tfm(struct crypto_skcipher *tfm)
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
191
  {
043a44001   Herbert Xu   crypto: pcbc - Co...
192
  	struct crypto_pcbc_ctx *ctx = crypto_skcipher_ctx(tfm);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
193
194
  	crypto_free_cipher(ctx->child);
  }
043a44001   Herbert Xu   crypto: pcbc - Co...
195
196
197
198
199
200
201
  static void crypto_pcbc_free(struct skcipher_instance *inst)
  {
  	crypto_drop_skcipher(skcipher_instance_ctx(inst));
  	kfree(inst);
  }
  
  static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb)
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
202
  {
043a44001   Herbert Xu   crypto: pcbc - Co...
203
204
205
  	struct skcipher_instance *inst;
  	struct crypto_attr_type *algt;
  	struct crypto_spawn *spawn;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
206
  	struct crypto_alg *alg;
ebc610e5b   Herbert Xu   [CRYPTO] template...
207
  	int err;
043a44001   Herbert Xu   crypto: pcbc - Co...
208
209
210
211
212
213
214
  	algt = crypto_get_attr_type(tb);
  	if (IS_ERR(algt))
  		return PTR_ERR(algt);
  
  	if (((algt->type ^ CRYPTO_ALG_TYPE_SKCIPHER) & algt->mask) &
  	    ~CRYPTO_ALG_INTERNAL)
  		return -EINVAL;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
215

043a44001   Herbert Xu   crypto: pcbc - Co...
216
217
218
219
220
221
222
223
224
  	inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL);
  	if (!inst)
  		return -ENOMEM;
  
  	alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER |
  				      (algt->type & CRYPTO_ALG_INTERNAL),
  				  CRYPTO_ALG_TYPE_MASK |
  				  (algt->mask & CRYPTO_ALG_INTERNAL));
  	err = PTR_ERR(alg);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
225
  	if (IS_ERR(alg))
043a44001   Herbert Xu   crypto: pcbc - Co...
226
227
228
229
230
231
232
233
  		goto err_free_inst;
  
  	spawn = skcipher_instance_ctx(inst);
  	err = crypto_init_spawn(spawn, alg, skcipher_crypto_instance(inst),
  				CRYPTO_ALG_TYPE_MASK);
  	crypto_mod_put(alg);
  	if (err)
  		goto err_free_inst;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
234

043a44001   Herbert Xu   crypto: pcbc - Co...
235
236
237
  	err = crypto_inst_setname(skcipher_crypto_instance(inst), "pcbc", alg);
  	if (err)
  		goto err_drop_spawn;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
238

043a44001   Herbert Xu   crypto: pcbc - Co...
239
240
241
242
  	inst->alg.base.cra_flags = alg->cra_flags & CRYPTO_ALG_INTERNAL;
  	inst->alg.base.cra_priority = alg->cra_priority;
  	inst->alg.base.cra_blocksize = alg->cra_blocksize;
  	inst->alg.base.cra_alignmask = alg->cra_alignmask;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
243

043a44001   Herbert Xu   crypto: pcbc - Co...
244
245
246
  	inst->alg.ivsize = alg->cra_blocksize;
  	inst->alg.min_keysize = alg->cra_cipher.cia_min_keysize;
  	inst->alg.max_keysize = alg->cra_cipher.cia_max_keysize;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
247

043a44001   Herbert Xu   crypto: pcbc - Co...
248
  	inst->alg.base.cra_ctxsize = sizeof(struct crypto_pcbc_ctx);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
249

043a44001   Herbert Xu   crypto: pcbc - Co...
250
251
  	inst->alg.init = crypto_pcbc_init_tfm;
  	inst->alg.exit = crypto_pcbc_exit_tfm;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
252

043a44001   Herbert Xu   crypto: pcbc - Co...
253
254
255
  	inst->alg.setkey = crypto_pcbc_setkey;
  	inst->alg.encrypt = crypto_pcbc_encrypt;
  	inst->alg.decrypt = crypto_pcbc_decrypt;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
256

043a44001   Herbert Xu   crypto: pcbc - Co...
257
  	inst->free = crypto_pcbc_free;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
258

043a44001   Herbert Xu   crypto: pcbc - Co...
259
260
261
262
263
264
265
266
267
268
  	err = skcipher_register_instance(tmpl, inst);
  	if (err)
  		goto err_drop_spawn;
  
  out:
  	return err;
  
  err_drop_spawn:
  	crypto_drop_spawn(spawn);
  err_free_inst:
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
269
  	kfree(inst);
043a44001   Herbert Xu   crypto: pcbc - Co...
270
  	goto out;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
271
272
273
274
  }
  
  static struct crypto_template crypto_pcbc_tmpl = {
  	.name = "pcbc",
043a44001   Herbert Xu   crypto: pcbc - Co...
275
  	.create = crypto_pcbc_create,
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
  	.module = THIS_MODULE,
  };
  
  static int __init crypto_pcbc_module_init(void)
  {
  	return crypto_register_template(&crypto_pcbc_tmpl);
  }
  
  static void __exit crypto_pcbc_module_exit(void)
  {
  	crypto_unregister_template(&crypto_pcbc_tmpl);
  }
  
  module_init(crypto_pcbc_module_init);
  module_exit(crypto_pcbc_module_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("PCBC block cipher algorithm");
4943ba16b   Kees Cook   crypto: include c...
294
  MODULE_ALIAS_CRYPTO("pcbc");