Blame view

crypto/pcbc.c 7.7 KB
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  /*
   * 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.
   *
   */
  
  #include <crypto/algapi.h>
  #include <linux/err.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/scatterlist.h>
  #include <linux/slab.h>
  
  struct crypto_pcbc_ctx {
  	struct crypto_cipher *child;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  };
  
  static int crypto_pcbc_setkey(struct crypto_tfm *parent, const u8 *key,
  			      unsigned int keylen)
  {
  	struct crypto_pcbc_ctx *ctx = crypto_tfm_ctx(parent);
  	struct crypto_cipher *child = ctx->child;
  	int err;
  
  	crypto_cipher_clear_flags(child, CRYPTO_TFM_REQ_MASK);
  	crypto_cipher_set_flags(child, crypto_tfm_get_flags(parent) &
  				CRYPTO_TFM_REQ_MASK);
  	err = crypto_cipher_setkey(child, key, keylen);
  	crypto_tfm_set_flags(parent, crypto_cipher_get_flags(child) &
  			     CRYPTO_TFM_RES_MASK);
  	return err;
  }
  
  static int crypto_pcbc_encrypt_segment(struct blkcipher_desc *desc,
  				       struct blkcipher_walk *walk,
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
47
  				       struct crypto_cipher *tfm)
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
48
49
50
51
52
53
54
55
56
57
  {
  	void (*fn)(struct crypto_tfm *, u8 *, const u8 *) =
  		crypto_cipher_alg(tfm)->cia_encrypt;
  	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...
58
  		crypto_xor(iv, src, bsize);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
59
60
  		fn(crypto_cipher_tfm(tfm), dst, iv);
  		memcpy(iv, dst, bsize);
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
61
  		crypto_xor(iv, src, bsize);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
62
63
64
65
66
67
68
69
70
71
  
  		src += bsize;
  		dst += bsize;
  	} while ((nbytes -= bsize) >= bsize);
  
  	return nbytes;
  }
  
  static int crypto_pcbc_encrypt_inplace(struct blkcipher_desc *desc,
  				       struct blkcipher_walk *walk,
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
72
  				       struct crypto_cipher *tfm)
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
73
74
75
76
77
78
79
80
81
82
83
  {
  	void (*fn)(struct crypto_tfm *, u8 *, const u8 *) =
  		crypto_cipher_alg(tfm)->cia_encrypt;
  	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...
84
  		crypto_xor(iv, src, bsize);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
85
  		fn(crypto_cipher_tfm(tfm), src, iv);
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
86
87
  		memcpy(iv, tmpbuf, bsize);
  		crypto_xor(iv, src, bsize);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
  
  		src += bsize;
  	} while ((nbytes -= bsize) >= bsize);
  
  	memcpy(walk->iv, iv, bsize);
  
  	return nbytes;
  }
  
  static int crypto_pcbc_encrypt(struct blkcipher_desc *desc,
  			       struct scatterlist *dst, struct scatterlist *src,
  			       unsigned int nbytes)
  {
  	struct blkcipher_walk walk;
  	struct crypto_blkcipher *tfm = desc->tfm;
  	struct crypto_pcbc_ctx *ctx = crypto_blkcipher_ctx(tfm);
  	struct crypto_cipher *child = ctx->child;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
105
106
107
108
109
110
111
  	int err;
  
  	blkcipher_walk_init(&walk, dst, src, nbytes);
  	err = blkcipher_walk_virt(desc, &walk);
  
  	while ((nbytes = walk.nbytes)) {
  		if (walk.src.virt.addr == walk.dst.virt.addr)
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
112
113
  			nbytes = crypto_pcbc_encrypt_inplace(desc, &walk,
  							     child);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
114
  		else
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
115
116
  			nbytes = crypto_pcbc_encrypt_segment(desc, &walk,
  							     child);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
117
118
119
120
121
122
123
124
  		err = blkcipher_walk_done(desc, &walk, nbytes);
  	}
  
  	return err;
  }
  
  static int crypto_pcbc_decrypt_segment(struct blkcipher_desc *desc,
  				       struct blkcipher_walk *walk,
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
125
  				       struct crypto_cipher *tfm)
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
126
127
128
129
130
131
132
133
134
135
136
  {
  	void (*fn)(struct crypto_tfm *, u8 *, const u8 *) =
  		crypto_cipher_alg(tfm)->cia_decrypt;
  	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 {
  		fn(crypto_cipher_tfm(tfm), dst, src);
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
137
  		crypto_xor(dst, iv, bsize);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
138
  		memcpy(iv, src, bsize);
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
139
  		crypto_xor(iv, dst, bsize);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
140
141
142
143
144
145
146
147
148
149
150
151
  
  		src += bsize;
  		dst += bsize;
  	} while ((nbytes -= bsize) >= bsize);
  
  	memcpy(walk->iv, iv, bsize);
  
  	return nbytes;
  }
  
  static int crypto_pcbc_decrypt_inplace(struct blkcipher_desc *desc,
  				       struct blkcipher_walk *walk,
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
152
  				       struct crypto_cipher *tfm)
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
153
154
155
156
157
158
159
160
161
162
163
164
  {
  	void (*fn)(struct crypto_tfm *, u8 *, const u8 *) =
  		crypto_cipher_alg(tfm)->cia_decrypt;
  	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);
  		fn(crypto_cipher_tfm(tfm), src, src);
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
165
  		crypto_xor(src, iv, bsize);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
166
  		memcpy(iv, tmpbuf, bsize);
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
167
  		crypto_xor(iv, src, bsize);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
  
  		src += bsize;
  	} while ((nbytes -= bsize) >= bsize);
  
  	memcpy(walk->iv, iv, bsize);
  
  	return nbytes;
  }
  
  static int crypto_pcbc_decrypt(struct blkcipher_desc *desc,
  			       struct scatterlist *dst, struct scatterlist *src,
  			       unsigned int nbytes)
  {
  	struct blkcipher_walk walk;
  	struct crypto_blkcipher *tfm = desc->tfm;
  	struct crypto_pcbc_ctx *ctx = crypto_blkcipher_ctx(tfm);
  	struct crypto_cipher *child = ctx->child;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
185
186
187
188
189
190
191
  	int err;
  
  	blkcipher_walk_init(&walk, dst, src, nbytes);
  	err = blkcipher_walk_virt(desc, &walk);
  
  	while ((nbytes = walk.nbytes)) {
  		if (walk.src.virt.addr == walk.dst.virt.addr)
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
192
193
  			nbytes = crypto_pcbc_decrypt_inplace(desc, &walk,
  							     child);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
194
  		else
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
195
196
  			nbytes = crypto_pcbc_decrypt_segment(desc, &walk,
  							     child);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
197
198
199
200
201
  		err = blkcipher_walk_done(desc, &walk, nbytes);
  	}
  
  	return err;
  }
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
202
203
204
205
206
  static int crypto_pcbc_init_tfm(struct crypto_tfm *tfm)
  {
  	struct crypto_instance *inst = (void *)tfm->__crt_alg;
  	struct crypto_spawn *spawn = crypto_instance_ctx(inst);
  	struct crypto_pcbc_ctx *ctx = crypto_tfm_ctx(tfm);
2e306ee01   Herbert Xu   [CRYPTO] api: Add...
207
  	struct crypto_cipher *cipher;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
208

2e306ee01   Herbert Xu   [CRYPTO] api: Add...
209
210
211
  	cipher = crypto_spawn_cipher(spawn);
  	if (IS_ERR(cipher))
  		return PTR_ERR(cipher);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
212

2e306ee01   Herbert Xu   [CRYPTO] api: Add...
213
  	ctx->child = cipher;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
214
215
216
217
218
219
220
221
  	return 0;
  }
  
  static void crypto_pcbc_exit_tfm(struct crypto_tfm *tfm)
  {
  	struct crypto_pcbc_ctx *ctx = crypto_tfm_ctx(tfm);
  	crypto_free_cipher(ctx->child);
  }
ebc610e5b   Herbert Xu   [CRYPTO] template...
222
  static struct crypto_instance *crypto_pcbc_alloc(struct rtattr **tb)
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
223
224
225
  {
  	struct crypto_instance *inst;
  	struct crypto_alg *alg;
ebc610e5b   Herbert Xu   [CRYPTO] template...
226
227
228
229
230
  	int err;
  
  	err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_BLKCIPHER);
  	if (err)
  		return ERR_PTR(err);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
231

ebc610e5b   Herbert Xu   [CRYPTO] template...
232
233
  	alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER,
  				  CRYPTO_ALG_TYPE_MASK);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
234
  	if (IS_ERR(alg))
e231c2ee6   David Howells   Convert ERR_PTR(P...
235
  		return ERR_CAST(alg);
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
236
237
238
239
240
241
242
243
244
245
  
  	inst = crypto_alloc_instance("pcbc", alg);
  	if (IS_ERR(inst))
  		goto out_put_alg;
  
  	inst->alg.cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER;
  	inst->alg.cra_priority = alg->cra_priority;
  	inst->alg.cra_blocksize = alg->cra_blocksize;
  	inst->alg.cra_alignmask = alg->cra_alignmask;
  	inst->alg.cra_type = &crypto_blkcipher_type;
d0b9007a2   Herbert Xu   [CRYPTO] pcbc: Us...
246
247
  	/* We access the data as u32s when xoring. */
  	inst->alg.cra_alignmask |= __alignof__(u32) - 1;
91652be5d   David Howells   [CRYPTO] pcbc: Ad...
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
  	inst->alg.cra_blkcipher.ivsize = alg->cra_blocksize;
  	inst->alg.cra_blkcipher.min_keysize = alg->cra_cipher.cia_min_keysize;
  	inst->alg.cra_blkcipher.max_keysize = alg->cra_cipher.cia_max_keysize;
  
  	inst->alg.cra_ctxsize = sizeof(struct crypto_pcbc_ctx);
  
  	inst->alg.cra_init = crypto_pcbc_init_tfm;
  	inst->alg.cra_exit = crypto_pcbc_exit_tfm;
  
  	inst->alg.cra_blkcipher.setkey = crypto_pcbc_setkey;
  	inst->alg.cra_blkcipher.encrypt = crypto_pcbc_encrypt;
  	inst->alg.cra_blkcipher.decrypt = crypto_pcbc_decrypt;
  
  out_put_alg:
  	crypto_mod_put(alg);
  	return inst;
  }
  
  static void crypto_pcbc_free(struct crypto_instance *inst)
  {
  	crypto_drop_spawn(crypto_instance_ctx(inst));
  	kfree(inst);
  }
  
  static struct crypto_template crypto_pcbc_tmpl = {
  	.name = "pcbc",
  	.alloc = crypto_pcbc_alloc,
  	.free = crypto_pcbc_free,
  	.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");