aboutsummaryrefslogtreecommitdiffstats
path: root/crypto/eseqiv.c
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2007-12-04 20:10:53 -0500
committerHerbert Xu <herbert@gondor.apana.org.au>2008-01-10 16:16:45 -0500
commit806d183aa6cc565d0f6bd2fb7fc6bfb175cc4813 (patch)
treeb544746f70bda6413105887fe1e6d28399840e23 /crypto/eseqiv.c
parent15c67286685cddce207b646306e8819ec8268ede (diff)
[CRYPTO] eseqiv: Add 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. It has one noteworthy property that for IPsec the IV happens to lie just before the plain text so the IV generation simply increases the number of encrypted blocks by one. Therefore the cost of this generator is entirely dependent on the speed of the underlying cipher. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'crypto/eseqiv.c')
-rw-r--r--crypto/eseqiv.c264
1 files changed, 264 insertions, 0 deletions
diff --git a/crypto/eseqiv.c b/crypto/eseqiv.c
new file mode 100644
index 000000000000..eb90d27ae118
--- /dev/null
+++ b/crypto/eseqiv.c
@@ -0,0 +1,264 @@
1/*
2 * eseqiv: Encrypted Sequence Number IV Generator
3 *
4 * This generator generates an IV based on a sequence number by xoring it
5 * with a salt and then encrypting it with the same key as used to encrypt
6 * the plain text. This algorithm requires that the block size be equal
7 * to the IV size. It is mainly useful for CBC.
8 *
9 * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au>
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the Free
13 * Software Foundation; either version 2 of the License, or (at your option)
14 * any later version.
15 *
16 */
17
18#include <crypto/internal/skcipher.h>
19#include <crypto/scatterwalk.h>
20#include <linux/err.h>
21#include <linux/init.h>
22#include <linux/kernel.h>
23#include <linux/mm.h>
24#include <linux/module.h>
25#include <linux/random.h>
26#include <linux/scatterlist.h>
27#include <linux/spinlock.h>
28#include <linux/string.h>
29
30struct eseqiv_request_ctx {
31 struct scatterlist src[2];
32 struct scatterlist dst[2];
33 char tail[];
34};
35
36struct eseqiv_ctx {
37 spinlock_t lock;
38 unsigned int reqoff;
39 char salt[];
40};
41
42static void eseqiv_complete2(struct skcipher_givcrypt_request *req)
43{
44 struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
45 struct eseqiv_request_ctx *reqctx = skcipher_givcrypt_reqctx(req);
46
47 memcpy(req->giv, PTR_ALIGN((u8 *)reqctx->tail,
48 crypto_ablkcipher_alignmask(geniv) + 1),
49 crypto_ablkcipher_ivsize(geniv));
50}
51
52static void eseqiv_complete(struct crypto_async_request *base, int err)
53{
54 struct skcipher_givcrypt_request *req = base->data;
55
56 if (err)
57 goto out;
58
59 eseqiv_complete2(req);
60
61out:
62 skcipher_givcrypt_complete(req, err);
63}
64
65static void eseqiv_chain(struct scatterlist *head, struct scatterlist *sg,
66 int chain)
67{
68 if (chain) {
69 head->length += sg->length;
70 sg = scatterwalk_sg_next(sg);
71 }
72
73 if (sg)
74 scatterwalk_sg_chain(head, 2, sg);
75 else
76 sg_mark_end(head);
77}
78
79static int eseqiv_givencrypt(struct skcipher_givcrypt_request *req)
80{
81 struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
82 struct eseqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
83 struct eseqiv_request_ctx *reqctx = skcipher_givcrypt_reqctx(req);
84 struct ablkcipher_request *subreq;
85 crypto_completion_t complete;
86 void *data;
87 struct scatterlist *osrc, *odst;
88 struct scatterlist *dst;
89 struct page *srcp;
90 struct page *dstp;
91 u8 *giv;
92 u8 *vsrc;
93 u8 *vdst;
94 __be64 seq;
95 unsigned int ivsize;
96 unsigned int len;
97 int err;
98
99 subreq = (void *)(reqctx->tail + ctx->reqoff);
100 ablkcipher_request_set_tfm(subreq, skcipher_geniv_cipher(geniv));
101
102 giv = req->giv;
103 complete = req->creq.base.complete;
104 data = req->creq.base.data;
105
106 osrc = req->creq.src;
107 odst = req->creq.dst;
108 srcp = sg_page(osrc);
109 dstp = sg_page(odst);
110 vsrc = PageHighMem(srcp) ? NULL : page_address(srcp) + osrc->offset;
111 vdst = PageHighMem(dstp) ? NULL : page_address(dstp) + odst->offset;
112
113 ivsize = crypto_ablkcipher_ivsize(geniv);
114
115 if (vsrc != giv + ivsize && vdst != giv + ivsize) {
116 giv = PTR_ALIGN((u8 *)reqctx->tail,
117 crypto_ablkcipher_alignmask(geniv) + 1);
118 complete = eseqiv_complete;
119 data = req;
120 }
121
122 ablkcipher_request_set_callback(subreq, req->creq.base.flags, complete,
123 data);
124
125 sg_init_table(reqctx->src, 2);
126 sg_set_buf(reqctx->src, giv, ivsize);
127 eseqiv_chain(reqctx->src, osrc, vsrc == giv + ivsize);
128
129 dst = reqctx->src;
130 if (osrc != odst) {
131 sg_init_table(reqctx->dst, 2);
132 sg_set_buf(reqctx->dst, giv, ivsize);
133 eseqiv_chain(reqctx->dst, odst, vdst == giv + ivsize);
134
135 dst = reqctx->dst;
136 }
137
138 ablkcipher_request_set_crypt(subreq, reqctx->src, dst,
139 req->creq.nbytes, req->creq.info);
140
141 memcpy(req->creq.info, ctx->salt, ivsize);
142
143 len = ivsize;
144 if (ivsize > sizeof(u64)) {
145 memset(req->giv, 0, ivsize - sizeof(u64));
146 len = sizeof(u64);
147 }
148 seq = cpu_to_be64(req->seq);
149 memcpy(req->giv + ivsize - len, &seq, len);
150
151 err = crypto_ablkcipher_encrypt(subreq);
152 if (err)
153 goto out;
154
155 eseqiv_complete2(req);
156
157out:
158 return err;
159}
160
161static int eseqiv_givencrypt_first(struct skcipher_givcrypt_request *req)
162{
163 struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
164 struct eseqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
165
166 spin_lock_bh(&ctx->lock);
167 if (crypto_ablkcipher_crt(geniv)->givencrypt != eseqiv_givencrypt_first)
168 goto unlock;
169
170 crypto_ablkcipher_crt(geniv)->givencrypt = eseqiv_givencrypt;
171 get_random_bytes(ctx->salt, crypto_ablkcipher_ivsize(geniv));
172
173unlock:
174 spin_unlock_bh(&ctx->lock);
175
176 return eseqiv_givencrypt(req);
177}
178
179static int eseqiv_init(struct crypto_tfm *tfm)
180{
181 struct crypto_ablkcipher *geniv = __crypto_ablkcipher_cast(tfm);
182 struct eseqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
183 unsigned long alignmask;
184 unsigned int reqsize;
185
186 spin_lock_init(&ctx->lock);
187
188 alignmask = crypto_tfm_ctx_alignment() - 1;
189 reqsize = sizeof(struct eseqiv_request_ctx);
190
191 if (alignmask & reqsize) {
192 alignmask &= reqsize;
193 alignmask--;
194 }
195
196 alignmask = ~alignmask;
197 alignmask &= crypto_ablkcipher_alignmask(geniv);
198
199 reqsize += alignmask;
200 reqsize += crypto_ablkcipher_ivsize(geniv);
201 reqsize = ALIGN(reqsize, crypto_tfm_ctx_alignment());
202
203 ctx->reqoff = reqsize - sizeof(struct eseqiv_request_ctx);
204
205 tfm->crt_ablkcipher.reqsize = reqsize +
206 sizeof(struct ablkcipher_request);
207
208 return skcipher_geniv_init(tfm);
209}
210
211static struct crypto_template eseqiv_tmpl;
212
213static struct crypto_instance *eseqiv_alloc(struct rtattr **tb)
214{
215 struct crypto_instance *inst;
216 int err;
217
218 inst = skcipher_geniv_alloc(&eseqiv_tmpl, tb, 0, 0);
219 if (IS_ERR(inst))
220 goto out;
221
222 err = -EINVAL;
223 if (inst->alg.cra_ablkcipher.ivsize != inst->alg.cra_blocksize)
224 goto free_inst;
225
226 inst->alg.cra_ablkcipher.givencrypt = eseqiv_givencrypt_first;
227
228 inst->alg.cra_init = eseqiv_init;
229 inst->alg.cra_exit = skcipher_geniv_exit;
230
231 inst->alg.cra_ctxsize = sizeof(struct eseqiv_ctx);
232 inst->alg.cra_ctxsize += inst->alg.cra_ablkcipher.ivsize;
233
234out:
235 return inst;
236
237free_inst:
238 skcipher_geniv_free(inst);
239 inst = ERR_PTR(err);
240 goto out;
241}
242
243static struct crypto_template eseqiv_tmpl = {
244 .name = "eseqiv",
245 .alloc = eseqiv_alloc,
246 .free = skcipher_geniv_free,
247 .module = THIS_MODULE,
248};
249
250static int __init eseqiv_module_init(void)
251{
252 return crypto_register_template(&eseqiv_tmpl);
253}
254
255static void __exit eseqiv_module_exit(void)
256{
257 crypto_unregister_template(&eseqiv_tmpl);
258}
259
260module_init(eseqiv_module_init);
261module_exit(eseqiv_module_exit);
262
263MODULE_LICENSE("GPL");
264MODULE_DESCRIPTION("Encrypted Sequence Number IV Generator");