Blame view

crypto/cbc.c 7.44 KB
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  /*
   * CBC: Cipher Block Chaining mode
   *
   * 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>
50b6544e1   Herbert Xu   [CRYPTO] cbc: Req...
17
  #include <linux/log2.h>
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
18
19
20
21
22
23
  #include <linux/module.h>
  #include <linux/scatterlist.h>
  #include <linux/slab.h>
  
  struct crypto_cbc_ctx {
  	struct crypto_cipher *child;
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  };
  
  static int crypto_cbc_setkey(struct crypto_tfm *parent, const u8 *key,
  			     unsigned int keylen)
  {
  	struct crypto_cbc_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_cbc_encrypt_segment(struct blkcipher_desc *desc,
  				      struct blkcipher_walk *walk,
3c7f076da   Herbert Xu   [CRYPTO] cbc: Use...
44
  				      struct crypto_cipher *tfm)
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
45
46
47
48
49
50
51
52
53
54
  {
  	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 {
3c7f076da   Herbert Xu   [CRYPTO] cbc: Use...
55
  		crypto_xor(iv, src, bsize);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
56
57
58
59
60
61
62
63
64
65
66
67
  		fn(crypto_cipher_tfm(tfm), dst, iv);
  		memcpy(iv, dst, bsize);
  
  		src += bsize;
  		dst += bsize;
  	} while ((nbytes -= bsize) >= bsize);
  
  	return nbytes;
  }
  
  static int crypto_cbc_encrypt_inplace(struct blkcipher_desc *desc,
  				      struct blkcipher_walk *walk,
3c7f076da   Herbert Xu   [CRYPTO] cbc: Use...
68
  				      struct crypto_cipher *tfm)
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
69
70
71
72
73
74
75
76
77
  {
  	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;
  
  	do {
3c7f076da   Herbert Xu   [CRYPTO] cbc: Use...
78
  		crypto_xor(src, iv, bsize);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  		fn(crypto_cipher_tfm(tfm), src, src);
  		iv = src;
  
  		src += bsize;
  	} while ((nbytes -= bsize) >= bsize);
  
  	memcpy(walk->iv, iv, bsize);
  
  	return nbytes;
  }
  
  static int crypto_cbc_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_cbc_ctx *ctx = crypto_blkcipher_ctx(tfm);
  	struct crypto_cipher *child = ctx->child;
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
98
99
100
101
102
103
104
  	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)
3c7f076da   Herbert Xu   [CRYPTO] cbc: Use...
105
  			nbytes = crypto_cbc_encrypt_inplace(desc, &walk, child);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
106
  		else
3c7f076da   Herbert Xu   [CRYPTO] cbc: Use...
107
  			nbytes = crypto_cbc_encrypt_segment(desc, &walk, child);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
108
109
110
111
112
113
114
115
  		err = blkcipher_walk_done(desc, &walk, nbytes);
  	}
  
  	return err;
  }
  
  static int crypto_cbc_decrypt_segment(struct blkcipher_desc *desc,
  				      struct blkcipher_walk *walk,
3c7f076da   Herbert Xu   [CRYPTO] cbc: Use...
116
  				      struct crypto_cipher *tfm)
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
117
118
119
120
121
122
123
124
125
126
127
  {
  	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);
3c7f076da   Herbert Xu   [CRYPTO] cbc: Use...
128
  		crypto_xor(dst, iv, bsize);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
129
130
131
132
133
134
135
136
137
138
139
140
141
  		iv = src;
  
  		src += bsize;
  		dst += bsize;
  	} while ((nbytes -= bsize) >= bsize);
  
  	memcpy(walk->iv, iv, bsize);
  
  	return nbytes;
  }
  
  static int crypto_cbc_decrypt_inplace(struct blkcipher_desc *desc,
  				      struct blkcipher_walk *walk,
3c7f076da   Herbert Xu   [CRYPTO] cbc: Use...
142
  				      struct crypto_cipher *tfm)
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
143
144
145
146
  {
  	void (*fn)(struct crypto_tfm *, u8 *, const u8 *) =
  		crypto_cipher_alg(tfm)->cia_decrypt;
  	int bsize = crypto_cipher_blocksize(tfm);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
147
148
  	unsigned int nbytes = walk->nbytes;
  	u8 *src = walk->src.virt.addr;
50b6544e1   Herbert Xu   [CRYPTO] cbc: Req...
149
  	u8 last_iv[bsize];
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
150
151
  
  	/* Start of the last block. */
50b6544e1   Herbert Xu   [CRYPTO] cbc: Req...
152
153
  	src += nbytes - (nbytes & (bsize - 1)) - bsize;
  	memcpy(last_iv, src, bsize);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
154
155
156
157
158
  
  	for (;;) {
  		fn(crypto_cipher_tfm(tfm), src, src);
  		if ((nbytes -= bsize) < bsize)
  			break;
3c7f076da   Herbert Xu   [CRYPTO] cbc: Use...
159
  		crypto_xor(src, src - bsize, bsize);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
160
161
  		src -= bsize;
  	}
50b6544e1   Herbert Xu   [CRYPTO] cbc: Req...
162
163
  	crypto_xor(src, walk->iv, bsize);
  	memcpy(walk->iv, last_iv, bsize);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
164
165
166
167
168
169
170
171
172
173
174
175
  
  	return nbytes;
  }
  
  static int crypto_cbc_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_cbc_ctx *ctx = crypto_blkcipher_ctx(tfm);
  	struct crypto_cipher *child = ctx->child;
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
176
177
178
179
180
181
182
  	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)
3c7f076da   Herbert Xu   [CRYPTO] cbc: Use...
183
  			nbytes = crypto_cbc_decrypt_inplace(desc, &walk, child);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
184
  		else
3c7f076da   Herbert Xu   [CRYPTO] cbc: Use...
185
  			nbytes = crypto_cbc_decrypt_segment(desc, &walk, child);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
186
187
188
189
190
  		err = blkcipher_walk_done(desc, &walk, nbytes);
  	}
  
  	return err;
  }
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
191
192
193
194
195
  static int crypto_cbc_init_tfm(struct crypto_tfm *tfm)
  {
  	struct crypto_instance *inst = (void *)tfm->__crt_alg;
  	struct crypto_spawn *spawn = crypto_instance_ctx(inst);
  	struct crypto_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
2e306ee01   Herbert Xu   [CRYPTO] api: Add...
196
  	struct crypto_cipher *cipher;
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
197

2e306ee01   Herbert Xu   [CRYPTO] api: Add...
198
199
200
  	cipher = crypto_spawn_cipher(spawn);
  	if (IS_ERR(cipher))
  		return PTR_ERR(cipher);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
201

2e306ee01   Herbert Xu   [CRYPTO] api: Add...
202
  	ctx->child = cipher;
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
203
204
205
206
207
208
209
210
  	return 0;
  }
  
  static void crypto_cbc_exit_tfm(struct crypto_tfm *tfm)
  {
  	struct crypto_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
  	crypto_free_cipher(ctx->child);
  }
ebc610e5b   Herbert Xu   [CRYPTO] template...
211
  static struct crypto_instance *crypto_cbc_alloc(struct rtattr **tb)
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
212
213
214
  {
  	struct crypto_instance *inst;
  	struct crypto_alg *alg;
ebc610e5b   Herbert Xu   [CRYPTO] template...
215
216
217
218
219
  	int err;
  
  	err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_BLKCIPHER);
  	if (err)
  		return ERR_PTR(err);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
220

ebc610e5b   Herbert Xu   [CRYPTO] template...
221
222
  	alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER,
  				  CRYPTO_ALG_TYPE_MASK);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
223
  	if (IS_ERR(alg))
e231c2ee6   David Howells   Convert ERR_PTR(P...
224
  		return ERR_CAST(alg);
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
225

50b6544e1   Herbert Xu   [CRYPTO] cbc: Req...
226
227
228
  	inst = ERR_PTR(-EINVAL);
  	if (!is_power_of_2(alg->cra_blocksize))
  		goto out_put_alg;
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
229
230
231
232
233
234
235
236
237
  	inst = crypto_alloc_instance("cbc", 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;
3c7f076da   Herbert Xu   [CRYPTO] cbc: Use...
238
239
  	/* We access the data as u32s when xoring. */
  	inst->alg.cra_alignmask |= __alignof__(u32) - 1;
db131ef90   Herbert Xu   [CRYPTO] cipher: ...
240
241
242
243
244
245
246
247
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
  	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_cbc_ctx);
  
  	inst->alg.cra_init = crypto_cbc_init_tfm;
  	inst->alg.cra_exit = crypto_cbc_exit_tfm;
  
  	inst->alg.cra_blkcipher.setkey = crypto_cbc_setkey;
  	inst->alg.cra_blkcipher.encrypt = crypto_cbc_encrypt;
  	inst->alg.cra_blkcipher.decrypt = crypto_cbc_decrypt;
  
  out_put_alg:
  	crypto_mod_put(alg);
  	return inst;
  }
  
  static void crypto_cbc_free(struct crypto_instance *inst)
  {
  	crypto_drop_spawn(crypto_instance_ctx(inst));
  	kfree(inst);
  }
  
  static struct crypto_template crypto_cbc_tmpl = {
  	.name = "cbc",
  	.alloc = crypto_cbc_alloc,
  	.free = crypto_cbc_free,
  	.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);
  }
  
  module_init(crypto_cbc_module_init);
  module_exit(crypto_cbc_module_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("CBC block cipher algorithm");