Blame view

crypto/eseqiv.c 6.45 KB
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  /*
   * eseqiv: Encrypted Sequence Number IV Generator
   *
   * This generator generates an IV based on a sequence number by xoring it
   * with a salt and then encrypting it with the same key as used to encrypt
   * the plain text.  This algorithm requires that the block size be equal
   * to the IV size.  It is mainly useful for CBC.
   *
   * Copyright (c) 2007 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/internal/skcipher.h>
a0f000ec9   Herbert Xu   crypto: skcipher ...
19
  #include <crypto/rng.h>
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
20
21
22
23
24
25
  #include <crypto/scatterwalk.h>
  #include <linux/err.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
  #include <linux/mm.h>
  #include <linux/module.h>
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
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
  #include <linux/scatterlist.h>
  #include <linux/spinlock.h>
  #include <linux/string.h>
  
  struct eseqiv_request_ctx {
  	struct scatterlist src[2];
  	struct scatterlist dst[2];
  	char tail[];
  };
  
  struct eseqiv_ctx {
  	spinlock_t lock;
  	unsigned int reqoff;
  	char salt[];
  };
  
  static void eseqiv_complete2(struct skcipher_givcrypt_request *req)
  {
  	struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
  	struct eseqiv_request_ctx *reqctx = skcipher_givcrypt_reqctx(req);
  
  	memcpy(req->giv, PTR_ALIGN((u8 *)reqctx->tail,
  			 crypto_ablkcipher_alignmask(geniv) + 1),
  	       crypto_ablkcipher_ivsize(geniv));
  }
  
  static void eseqiv_complete(struct crypto_async_request *base, int err)
  {
  	struct skcipher_givcrypt_request *req = base->data;
  
  	if (err)
  		goto out;
  
  	eseqiv_complete2(req);
  
  out:
  	skcipher_givcrypt_complete(req, err);
  }
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
64
65
66
67
68
69
70
71
72
73
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
  static int eseqiv_givencrypt(struct skcipher_givcrypt_request *req)
  {
  	struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
  	struct eseqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
  	struct eseqiv_request_ctx *reqctx = skcipher_givcrypt_reqctx(req);
  	struct ablkcipher_request *subreq;
  	crypto_completion_t complete;
  	void *data;
  	struct scatterlist *osrc, *odst;
  	struct scatterlist *dst;
  	struct page *srcp;
  	struct page *dstp;
  	u8 *giv;
  	u8 *vsrc;
  	u8 *vdst;
  	__be64 seq;
  	unsigned int ivsize;
  	unsigned int len;
  	int err;
  
  	subreq = (void *)(reqctx->tail + ctx->reqoff);
  	ablkcipher_request_set_tfm(subreq, skcipher_geniv_cipher(geniv));
  
  	giv = req->giv;
  	complete = req->creq.base.complete;
  	data = req->creq.base.data;
  
  	osrc = req->creq.src;
  	odst = req->creq.dst;
  	srcp = sg_page(osrc);
  	dstp = sg_page(odst);
  	vsrc = PageHighMem(srcp) ? NULL : page_address(srcp) + osrc->offset;
  	vdst = PageHighMem(dstp) ? NULL : page_address(dstp) + odst->offset;
  
  	ivsize = crypto_ablkcipher_ivsize(geniv);
  
  	if (vsrc != giv + ivsize && vdst != giv + ivsize) {
  		giv = PTR_ALIGN((u8 *)reqctx->tail,
  				crypto_ablkcipher_alignmask(geniv) + 1);
  		complete = eseqiv_complete;
  		data = req;
  	}
  
  	ablkcipher_request_set_callback(subreq, req->creq.base.flags, complete,
  					data);
  
  	sg_init_table(reqctx->src, 2);
  	sg_set_buf(reqctx->src, giv, ivsize);
c920fa605   Steffen Klassert   crypto: Use scatt...
112
  	scatterwalk_crypto_chain(reqctx->src, osrc, vsrc == giv + ivsize, 2);
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
113
114
115
116
117
  
  	dst = reqctx->src;
  	if (osrc != odst) {
  		sg_init_table(reqctx->dst, 2);
  		sg_set_buf(reqctx->dst, giv, ivsize);
c920fa605   Steffen Klassert   crypto: Use scatt...
118
  		scatterwalk_crypto_chain(reqctx->dst, odst, vdst == giv + ivsize, 2);
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
119
120
121
122
123
  
  		dst = reqctx->dst;
  	}
  
  	ablkcipher_request_set_crypt(subreq, reqctx->src, dst,
46f8153cc   Herbert Xu   [CRYPTO] eseqiv: ...
124
125
  				     req->creq.nbytes + ivsize,
  				     req->creq.info);
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
126
127
128
129
130
131
132
133
134
135
136
137
138
139
  
  	memcpy(req->creq.info, ctx->salt, ivsize);
  
  	len = ivsize;
  	if (ivsize > sizeof(u64)) {
  		memset(req->giv, 0, ivsize - sizeof(u64));
  		len = sizeof(u64);
  	}
  	seq = cpu_to_be64(req->seq);
  	memcpy(req->giv + ivsize - len, &seq, len);
  
  	err = crypto_ablkcipher_encrypt(subreq);
  	if (err)
  		goto out;
abe5fa789   Steffen Klassert   crypto: eseqiv - ...
140
141
  	if (giv != req->giv)
  		eseqiv_complete2(req);
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
142
143
144
145
146
147
148
149
150
  
  out:
  	return err;
  }
  
  static int eseqiv_givencrypt_first(struct skcipher_givcrypt_request *req)
  {
  	struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
  	struct eseqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
a0f000ec9   Herbert Xu   crypto: skcipher ...
151
  	int err = 0;
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
152
153
154
155
156
157
  
  	spin_lock_bh(&ctx->lock);
  	if (crypto_ablkcipher_crt(geniv)->givencrypt != eseqiv_givencrypt_first)
  		goto unlock;
  
  	crypto_ablkcipher_crt(geniv)->givencrypt = eseqiv_givencrypt;
a0f000ec9   Herbert Xu   crypto: skcipher ...
158
159
  	err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
  				   crypto_ablkcipher_ivsize(geniv));
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
160
161
162
  
  unlock:
  	spin_unlock_bh(&ctx->lock);
a0f000ec9   Herbert Xu   crypto: skcipher ...
163
164
  	if (err)
  		return err;
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
  	return eseqiv_givencrypt(req);
  }
  
  static int eseqiv_init(struct crypto_tfm *tfm)
  {
  	struct crypto_ablkcipher *geniv = __crypto_ablkcipher_cast(tfm);
  	struct eseqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
  	unsigned long alignmask;
  	unsigned int reqsize;
  
  	spin_lock_init(&ctx->lock);
  
  	alignmask = crypto_tfm_ctx_alignment() - 1;
  	reqsize = sizeof(struct eseqiv_request_ctx);
  
  	if (alignmask & reqsize) {
  		alignmask &= reqsize;
  		alignmask--;
  	}
  
  	alignmask = ~alignmask;
  	alignmask &= crypto_ablkcipher_alignmask(geniv);
  
  	reqsize += alignmask;
  	reqsize += crypto_ablkcipher_ivsize(geniv);
  	reqsize = ALIGN(reqsize, crypto_tfm_ctx_alignment());
  
  	ctx->reqoff = reqsize - sizeof(struct eseqiv_request_ctx);
  
  	tfm->crt_ablkcipher.reqsize = reqsize +
  				      sizeof(struct ablkcipher_request);
  
  	return skcipher_geniv_init(tfm);
  }
  
  static struct crypto_template eseqiv_tmpl;
  
  static struct crypto_instance *eseqiv_alloc(struct rtattr **tb)
  {
  	struct crypto_instance *inst;
  	int err;
a0f000ec9   Herbert Xu   crypto: skcipher ...
206
207
208
  	err = crypto_get_default_rng();
  	if (err)
  		return ERR_PTR(err);
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
209
210
  	inst = skcipher_geniv_alloc(&eseqiv_tmpl, tb, 0, 0);
  	if (IS_ERR(inst))
a0f000ec9   Herbert Xu   crypto: skcipher ...
211
  		goto put_rng;
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
  
  	err = -EINVAL;
  	if (inst->alg.cra_ablkcipher.ivsize != inst->alg.cra_blocksize)
  		goto free_inst;
  
  	inst->alg.cra_ablkcipher.givencrypt = eseqiv_givencrypt_first;
  
  	inst->alg.cra_init = eseqiv_init;
  	inst->alg.cra_exit = skcipher_geniv_exit;
  
  	inst->alg.cra_ctxsize = sizeof(struct eseqiv_ctx);
  	inst->alg.cra_ctxsize += inst->alg.cra_ablkcipher.ivsize;
  
  out:
  	return inst;
  
  free_inst:
  	skcipher_geniv_free(inst);
  	inst = ERR_PTR(err);
a0f000ec9   Herbert Xu   crypto: skcipher ...
231
232
  put_rng:
  	crypto_put_default_rng();
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
233
234
  	goto out;
  }
a0f000ec9   Herbert Xu   crypto: skcipher ...
235
236
237
238
239
  static void eseqiv_free(struct crypto_instance *inst)
  {
  	skcipher_geniv_free(inst);
  	crypto_put_default_rng();
  }
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
240
241
242
  static struct crypto_template eseqiv_tmpl = {
  	.name = "eseqiv",
  	.alloc = eseqiv_alloc,
a0f000ec9   Herbert Xu   crypto: skcipher ...
243
  	.free = eseqiv_free,
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
244
245
  	.module = THIS_MODULE,
  };
5be5e667a   Herbert Xu   crypto: skcipher ...
246
  static int __init eseqiv_module_init(void)
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
247
248
249
  {
  	return crypto_register_template(&eseqiv_tmpl);
  }
5be5e667a   Herbert Xu   crypto: skcipher ...
250
  static void __exit eseqiv_module_exit(void)
806d183aa   Herbert Xu   [CRYPTO] eseqiv: ...
251
252
253
  {
  	crypto_unregister_template(&eseqiv_tmpl);
  }
5be5e667a   Herbert Xu   crypto: skcipher ...
254
255
256
257
258
259
  
  module_init(eseqiv_module_init);
  module_exit(eseqiv_module_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("Encrypted Sequence Number IV Generator");