Blame view

crypto/xcbc.c 9.04 KB
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   * Copyright (C)2006 USAGI/WIDE Project
   *
   * 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.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   *
   * Author:
   * 	Kazunori Miyazawa <miyazawa@linux-ipv6.org>
   */
42c271c6c   Herbert Xu   [CRYPTO] scatterw...
21
  #include <crypto/scatterwalk.h>
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
22
23
  #include <linux/crypto.h>
  #include <linux/err.h>
fb469840b   Herbert Xu   [CRYPTO] all: Che...
24
  #include <linux/hardirq.h>
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
25
26
27
28
29
  #include <linux/kernel.h>
  #include <linux/mm.h>
  #include <linux/rtnetlink.h>
  #include <linux/slab.h>
  #include <linux/scatterlist.h>
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
30

5b37538a5   Adrian Bunk   [CRYPTO] xcbc: Ma...
31
32
33
  static u_int32_t ks[12] = {0x01010101, 0x01010101, 0x01010101, 0x01010101,
  			   0x02020202, 0x02020202, 0x02020202, 0x02020202,
  			   0x03030303, 0x03030303, 0x03030303, 0x03030303};
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  /*
   * +------------------------
   * | <parent tfm>
   * +------------------------
   * | crypto_xcbc_ctx
   * +------------------------
   * | odds (block size)
   * +------------------------
   * | prev (block size)
   * +------------------------
   * | key (block size)
   * +------------------------
   * | consts (block size * 3)
   * +------------------------
   */
  struct crypto_xcbc_ctx {
6b701dde8   Herbert Xu   [CRYPTO] xcbc: Us...
50
  	struct crypto_cipher *child;
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  	u8 *odds;
  	u8 *prev;
  	u8 *key;
  	u8 *consts;
  	void (*xor)(u8 *a, const u8 *b, unsigned int bs);
  	unsigned int keylen;
  	unsigned int len;
  };
  
  static void xor_128(u8 *a, const u8 *b, unsigned int bs)
  {
  	((u32 *)a)[0] ^= ((u32 *)b)[0];
  	((u32 *)a)[1] ^= ((u32 *)b)[1];
  	((u32 *)a)[2] ^= ((u32 *)b)[2];
  	((u32 *)a)[3] ^= ((u32 *)b)[3];
  }
  
  static int _crypto_xcbc_digest_setkey(struct crypto_hash *parent,
  				      struct crypto_xcbc_ctx *ctx)
  {
  	int bs = crypto_hash_blocksize(parent);
  	int err = 0;
  	u8 key1[bs];
  
  	if ((err = crypto_cipher_setkey(ctx->child, ctx->key, ctx->keylen)))
  	    return err;
6b701dde8   Herbert Xu   [CRYPTO] xcbc: Us...
77
  	crypto_cipher_encrypt_one(ctx->child, key1, ctx->consts);
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
78
79
80
81
82
83
84
85
  
  	return crypto_cipher_setkey(ctx->child, key1, bs);
  }
  
  static int crypto_xcbc_digest_setkey(struct crypto_hash *parent,
  				     const u8 *inkey, unsigned int keylen)
  {
  	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(parent);
6b701dde8   Herbert Xu   [CRYPTO] xcbc: Us...
86
  	if (keylen != crypto_cipher_blocksize(ctx->child))
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
87
88
89
90
91
92
93
94
  		return -EINVAL;
  
  	ctx->keylen = keylen;
  	memcpy(ctx->key, inkey, keylen);
  	ctx->consts = (u8*)ks;
  
  	return _crypto_xcbc_digest_setkey(parent, ctx);
  }
5b37538a5   Adrian Bunk   [CRYPTO] xcbc: Ma...
95
  static int crypto_xcbc_digest_init(struct hash_desc *pdesc)
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
96
97
98
99
100
101
102
103
104
105
  {
  	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(pdesc->tfm);
  	int bs = crypto_hash_blocksize(pdesc->tfm);
  
  	ctx->len = 0;
  	memset(ctx->odds, 0, bs);
  	memset(ctx->prev, 0, bs);
  
  	return 0;
  }
fb469840b   Herbert Xu   [CRYPTO] all: Che...
106
107
108
  static int crypto_xcbc_digest_update2(struct hash_desc *pdesc,
  				      struct scatterlist *sg,
  				      unsigned int nbytes)
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
109
110
111
  {
  	struct crypto_hash *parent = pdesc->tfm;
  	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(parent);
6b701dde8   Herbert Xu   [CRYPTO] xcbc: Us...
112
  	struct crypto_cipher *tfm = ctx->child;
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
113
  	int bs = crypto_hash_blocksize(parent);
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
114

1edcf2e1e   Joy Latten   [CRYPTO] xcbc: Fi...
115
116
117
118
  	for (;;) {
  		struct page *pg = sg_page(sg);
  		unsigned int offset = sg->offset;
  		unsigned int slen = sg->length;
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
119

2f40a178e   Joy Latten   [CRYPTO] xcbc: Fi...
120
121
122
123
  		if (unlikely(slen > nbytes))
  			slen = nbytes;
  
  		nbytes -= slen;
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
  		while (slen > 0) {
  			unsigned int len = min(slen, ((unsigned int)(PAGE_SIZE)) - offset);
  			char *p = crypto_kmap(pg, 0) + offset;
  
  			/* checking the data can fill the block */
  			if ((ctx->len + len) <= bs) {
  				memcpy(ctx->odds + ctx->len, p, len);
  				ctx->len += len;
  				slen -= len;
  
  				/* checking the rest of the page */
  				if (len + offset >= PAGE_SIZE) {
  					offset = 0;
  					pg++;
  				} else
  					offset += len;
  
  				crypto_kunmap(p, 0);
6b701dde8   Herbert Xu   [CRYPTO] xcbc: Us...
142
  				crypto_yield(pdesc->flags);
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
143
144
145
146
147
148
149
150
151
  				continue;
  			}
  
  			/* filling odds with new data and encrypting it */
  			memcpy(ctx->odds + ctx->len, p, bs - ctx->len);
  			len -= bs - ctx->len;
  			p += bs - ctx->len;
  
  			ctx->xor(ctx->prev, ctx->odds, bs);
6b701dde8   Herbert Xu   [CRYPTO] xcbc: Us...
152
  			crypto_cipher_encrypt_one(tfm, ctx->prev, ctx->prev);
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
153
154
155
156
157
158
159
  
  			/* clearing the length */
  			ctx->len = 0;
  
  			/* encrypting the rest of data */
  			while (len > bs) {
  				ctx->xor(ctx->prev, p, bs);
6b701dde8   Herbert Xu   [CRYPTO] xcbc: Us...
160
161
  				crypto_cipher_encrypt_one(tfm, ctx->prev,
  							  ctx->prev);
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
162
163
164
165
166
167
168
169
170
171
  				p += bs;
  				len -= bs;
  			}
  
  			/* keeping the surplus of blocksize */
  			if (len) {
  				memcpy(ctx->odds, p, len);
  				ctx->len = len;
  			}
  			crypto_kunmap(p, 0);
6b701dde8   Herbert Xu   [CRYPTO] xcbc: Us...
172
  			crypto_yield(pdesc->flags);
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
173
174
175
176
  			slen -= min(slen, ((unsigned int)(PAGE_SIZE)) - offset);
  			offset = 0;
  			pg++;
  		}
1edcf2e1e   Joy Latten   [CRYPTO] xcbc: Fi...
177
178
179
180
181
  
  		if (!nbytes)
  			break;
  		sg = scatterwalk_sg_next(sg);
  	}
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
182
183
184
  
  	return 0;
  }
fb469840b   Herbert Xu   [CRYPTO] all: Che...
185
186
187
188
189
190
191
192
  static int crypto_xcbc_digest_update(struct hash_desc *pdesc,
  				     struct scatterlist *sg,
  				     unsigned int nbytes)
  {
  	if (WARN_ON_ONCE(in_irq()))
  		return -EDEADLK;
  	return crypto_xcbc_digest_update2(pdesc, sg, nbytes);
  }
5b37538a5   Adrian Bunk   [CRYPTO] xcbc: Ma...
193
  static int crypto_xcbc_digest_final(struct hash_desc *pdesc, u8 *out)
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
194
195
196
  {
  	struct crypto_hash *parent = pdesc->tfm;
  	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(parent);
6b701dde8   Herbert Xu   [CRYPTO] xcbc: Us...
197
  	struct crypto_cipher *tfm = ctx->child;
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
198
199
200
201
202
203
204
205
  	int bs = crypto_hash_blocksize(parent);
  	int err = 0;
  
  	if (ctx->len == bs) {
  		u8 key2[bs];
  
  		if ((err = crypto_cipher_setkey(tfm, ctx->key, ctx->keylen)) != 0)
  			return err;
6b701dde8   Herbert Xu   [CRYPTO] xcbc: Us...
206
207
  		crypto_cipher_encrypt_one(tfm, key2,
  					  (u8 *)(ctx->consts + bs));
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
208
209
210
211
  
  		ctx->xor(ctx->prev, ctx->odds, bs);
  		ctx->xor(ctx->prev, key2, bs);
  		_crypto_xcbc_digest_setkey(parent, ctx);
6b701dde8   Herbert Xu   [CRYPTO] xcbc: Us...
212
  		crypto_cipher_encrypt_one(tfm, out, ctx->prev);
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
213
214
215
216
217
218
219
220
221
222
223
224
225
  	} else {
  		u8 key3[bs];
  		unsigned int rlen;
  		u8 *p = ctx->odds + ctx->len;
  		*p = 0x80;
  		p++;
  
  		rlen = bs - ctx->len -1;
  		if (rlen)
  			memset(p, 0, rlen);
  
  		if ((err = crypto_cipher_setkey(tfm, ctx->key, ctx->keylen)) != 0)
  			return err;
6b701dde8   Herbert Xu   [CRYPTO] xcbc: Us...
226
227
  		crypto_cipher_encrypt_one(tfm, key3,
  					  (u8 *)(ctx->consts + bs * 2));
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
228
229
230
231
232
  
  		ctx->xor(ctx->prev, ctx->odds, bs);
  		ctx->xor(ctx->prev, key3, bs);
  
  		_crypto_xcbc_digest_setkey(parent, ctx);
6b701dde8   Herbert Xu   [CRYPTO] xcbc: Us...
233
  		crypto_cipher_encrypt_one(tfm, out, ctx->prev);
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
234
235
236
237
238
239
240
241
  	}
  
  	return 0;
  }
  
  static int crypto_xcbc_digest(struct hash_desc *pdesc,
  		  struct scatterlist *sg, unsigned int nbytes, u8 *out)
  {
fb469840b   Herbert Xu   [CRYPTO] all: Che...
242
243
  	if (WARN_ON_ONCE(in_irq()))
  		return -EDEADLK;
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
244
  	crypto_xcbc_digest_init(pdesc);
fb469840b   Herbert Xu   [CRYPTO] all: Che...
245
  	crypto_xcbc_digest_update2(pdesc, sg, nbytes);
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
246
247
248
249
250
  	return crypto_xcbc_digest_final(pdesc, out);
  }
  
  static int xcbc_init_tfm(struct crypto_tfm *tfm)
  {
2e306ee01   Herbert Xu   [CRYPTO] api: Add...
251
  	struct crypto_cipher *cipher;
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
252
253
254
255
  	struct crypto_instance *inst = (void *)tfm->__crt_alg;
  	struct crypto_spawn *spawn = crypto_instance_ctx(inst);
  	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(__crypto_hash_cast(tfm));
  	int bs = crypto_hash_blocksize(__crypto_hash_cast(tfm));
2e306ee01   Herbert Xu   [CRYPTO] api: Add...
256
257
258
  	cipher = crypto_spawn_cipher(spawn);
  	if (IS_ERR(cipher))
  		return PTR_ERR(cipher);
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
259
260
261
262
263
264
265
266
  
  	switch(bs) {
  	case 16:
  		ctx->xor = xor_128;
  		break;
  	default:
  		return -EINVAL;
  	}
2e306ee01   Herbert Xu   [CRYPTO] api: Add...
267
  	ctx->child = cipher;
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
268
269
270
271
272
273
274
275
276
277
278
279
  	ctx->odds = (u8*)(ctx+1);
  	ctx->prev = ctx->odds + bs;
  	ctx->key = ctx->prev + bs;
  
  	return 0;
  };
  
  static void xcbc_exit_tfm(struct crypto_tfm *tfm)
  {
  	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(__crypto_hash_cast(tfm));
  	crypto_free_cipher(ctx->child);
  }
ebc610e5b   Herbert Xu   [CRYPTO] template...
280
  static struct crypto_instance *xcbc_alloc(struct rtattr **tb)
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
281
282
283
  {
  	struct crypto_instance *inst;
  	struct crypto_alg *alg;
ebc610e5b   Herbert Xu   [CRYPTO] template...
284
285
286
287
288
289
290
291
  	int err;
  
  	err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_HASH);
  	if (err)
  		return ERR_PTR(err);
  
  	alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER,
  				  CRYPTO_ALG_TYPE_MASK);
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
292
  	if (IS_ERR(alg))
e231c2ee6   David Howells   Convert ERR_PTR(P...
293
  		return ERR_CAST(alg);
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
294
295
296
297
298
  
  	switch(alg->cra_blocksize) {
  	case 16:
  		break;
  	default:
1b87887d6   Herbert Xu   [CRYPTO] xcbc: Fi...
299
300
  		inst = ERR_PTR(-EINVAL);
  		goto out_put_alg;
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
301
302
303
304
305
306
307
308
309
310
311
  	}
  
  	inst = crypto_alloc_instance("xcbc", alg);
  	if (IS_ERR(inst))
  		goto out_put_alg;
  
  	inst->alg.cra_flags = CRYPTO_ALG_TYPE_HASH;
  	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_hash_type;
94765b9e4   Herbert Xu   [CRYPTO] xcbc: Re...
312
  	inst->alg.cra_hash.digestsize = alg->cra_blocksize;
333b0d7ee   Kazunori MIYAZAWA   [CRYPTO] xcbc: Ne...
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  	inst->alg.cra_ctxsize = sizeof(struct crypto_xcbc_ctx) +
  				ALIGN(inst->alg.cra_blocksize * 3, sizeof(void *));
  	inst->alg.cra_init = xcbc_init_tfm;
  	inst->alg.cra_exit = xcbc_exit_tfm;
  
  	inst->alg.cra_hash.init = crypto_xcbc_digest_init;
  	inst->alg.cra_hash.update = crypto_xcbc_digest_update;
  	inst->alg.cra_hash.final = crypto_xcbc_digest_final;
  	inst->alg.cra_hash.digest = crypto_xcbc_digest;
  	inst->alg.cra_hash.setkey = crypto_xcbc_digest_setkey;
  
  out_put_alg:
  	crypto_mod_put(alg);
  	return inst;
  }
  
  static void xcbc_free(struct crypto_instance *inst)
  {
  	crypto_drop_spawn(crypto_instance_ctx(inst));
  	kfree(inst);
  }
  
  static struct crypto_template crypto_xcbc_tmpl = {
  	.name = "xcbc",
  	.alloc = xcbc_alloc,
  	.free = xcbc_free,
  	.module = THIS_MODULE,
  };
  
  static int __init crypto_xcbc_module_init(void)
  {
  	return crypto_register_template(&crypto_xcbc_tmpl);
  }
  
  static void __exit crypto_xcbc_module_exit(void)
  {
  	crypto_unregister_template(&crypto_xcbc_tmpl);
  }
  
  module_init(crypto_xcbc_module_init);
  module_exit(crypto_xcbc_module_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("XCBC keyed hash algorithm");