diff options
author | Steffen Klassert <steffen.klassert@secunet.com> | 2011-03-07 19:04:58 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-03-13 23:22:27 -0400 |
commit | a5079d084f8be781aae8a635cab4b179cfea4ebd (patch) | |
tree | b2c19d6e9cfa9c47f653b6869b32eb0cd52c18b2 /crypto/authencesn.c | |
parent | 27b61ae2d73936554fcd07e77c3cc13bc88c41a9 (diff) |
crypto: authencesn - Add algorithm to handle IPsec extended sequence numbers
ESP with separate encryption/authentication algorithms needs a special
treatment for the associated data. This patch add a new algorithm that
handles esp with extended sequence numbers.
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'crypto/authencesn.c')
-rw-r--r-- | crypto/authencesn.c | 835 |
1 files changed, 835 insertions, 0 deletions
diff --git a/crypto/authencesn.c b/crypto/authencesn.c new file mode 100644 index 000000000000..136b68b9d8d4 --- /dev/null +++ b/crypto/authencesn.c | |||
@@ -0,0 +1,835 @@ | |||
1 | /* | ||
2 | * authencesn.c - AEAD wrapper for IPsec with extended sequence numbers, | ||
3 | * derived from authenc.c | ||
4 | * | ||
5 | * Copyright (C) 2010 secunet Security Networks AG | ||
6 | * Copyright (C) 2010 Steffen Klassert <steffen.klassert@secunet.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the Free | ||
10 | * Software Foundation; either version 2 of the License, or (at your option) | ||
11 | * any later version. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <crypto/aead.h> | ||
16 | #include <crypto/internal/hash.h> | ||
17 | #include <crypto/internal/skcipher.h> | ||
18 | #include <crypto/authenc.h> | ||
19 | #include <crypto/scatterwalk.h> | ||
20 | #include <linux/err.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/rtnetlink.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/spinlock.h> | ||
27 | |||
28 | struct authenc_esn_instance_ctx { | ||
29 | struct crypto_ahash_spawn auth; | ||
30 | struct crypto_skcipher_spawn enc; | ||
31 | }; | ||
32 | |||
33 | struct crypto_authenc_esn_ctx { | ||
34 | unsigned int reqoff; | ||
35 | struct crypto_ahash *auth; | ||
36 | struct crypto_ablkcipher *enc; | ||
37 | }; | ||
38 | |||
39 | struct authenc_esn_request_ctx { | ||
40 | unsigned int cryptlen; | ||
41 | unsigned int headlen; | ||
42 | unsigned int trailen; | ||
43 | struct scatterlist *sg; | ||
44 | struct scatterlist hsg[2]; | ||
45 | struct scatterlist tsg[1]; | ||
46 | struct scatterlist cipher[2]; | ||
47 | crypto_completion_t complete; | ||
48 | crypto_completion_t update_complete; | ||
49 | crypto_completion_t update_complete2; | ||
50 | char tail[]; | ||
51 | }; | ||
52 | |||
53 | static void authenc_esn_request_complete(struct aead_request *req, int err) | ||
54 | { | ||
55 | if (err != -EINPROGRESS) | ||
56 | aead_request_complete(req, err); | ||
57 | } | ||
58 | |||
59 | static int crypto_authenc_esn_setkey(struct crypto_aead *authenc_esn, const u8 *key, | ||
60 | unsigned int keylen) | ||
61 | { | ||
62 | unsigned int authkeylen; | ||
63 | unsigned int enckeylen; | ||
64 | struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn); | ||
65 | struct crypto_ahash *auth = ctx->auth; | ||
66 | struct crypto_ablkcipher *enc = ctx->enc; | ||
67 | struct rtattr *rta = (void *)key; | ||
68 | struct crypto_authenc_key_param *param; | ||
69 | int err = -EINVAL; | ||
70 | |||
71 | if (!RTA_OK(rta, keylen)) | ||
72 | goto badkey; | ||
73 | if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM) | ||
74 | goto badkey; | ||
75 | if (RTA_PAYLOAD(rta) < sizeof(*param)) | ||
76 | goto badkey; | ||
77 | |||
78 | param = RTA_DATA(rta); | ||
79 | enckeylen = be32_to_cpu(param->enckeylen); | ||
80 | |||
81 | key += RTA_ALIGN(rta->rta_len); | ||
82 | keylen -= RTA_ALIGN(rta->rta_len); | ||
83 | |||
84 | if (keylen < enckeylen) | ||
85 | goto badkey; | ||
86 | |||
87 | authkeylen = keylen - enckeylen; | ||
88 | |||
89 | crypto_ahash_clear_flags(auth, CRYPTO_TFM_REQ_MASK); | ||
90 | crypto_ahash_set_flags(auth, crypto_aead_get_flags(authenc_esn) & | ||
91 | CRYPTO_TFM_REQ_MASK); | ||
92 | err = crypto_ahash_setkey(auth, key, authkeylen); | ||
93 | crypto_aead_set_flags(authenc_esn, crypto_ahash_get_flags(auth) & | ||
94 | CRYPTO_TFM_RES_MASK); | ||
95 | |||
96 | if (err) | ||
97 | goto out; | ||
98 | |||
99 | crypto_ablkcipher_clear_flags(enc, CRYPTO_TFM_REQ_MASK); | ||
100 | crypto_ablkcipher_set_flags(enc, crypto_aead_get_flags(authenc_esn) & | ||
101 | CRYPTO_TFM_REQ_MASK); | ||
102 | err = crypto_ablkcipher_setkey(enc, key + authkeylen, enckeylen); | ||
103 | crypto_aead_set_flags(authenc_esn, crypto_ablkcipher_get_flags(enc) & | ||
104 | CRYPTO_TFM_RES_MASK); | ||
105 | |||
106 | out: | ||
107 | return err; | ||
108 | |||
109 | badkey: | ||
110 | crypto_aead_set_flags(authenc_esn, CRYPTO_TFM_RES_BAD_KEY_LEN); | ||
111 | goto out; | ||
112 | } | ||
113 | |||
114 | static void authenc_esn_geniv_ahash_update_done(struct crypto_async_request *areq, | ||
115 | int err) | ||
116 | { | ||
117 | struct aead_request *req = areq->data; | ||
118 | struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req); | ||
119 | struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn); | ||
120 | struct authenc_esn_request_ctx *areq_ctx = aead_request_ctx(req); | ||
121 | struct ahash_request *ahreq = (void *)(areq_ctx->tail + ctx->reqoff); | ||
122 | |||
123 | if (err) | ||
124 | goto out; | ||
125 | |||
126 | ahash_request_set_crypt(ahreq, areq_ctx->sg, ahreq->result, | ||
127 | areq_ctx->cryptlen); | ||
128 | ahash_request_set_callback(ahreq, aead_request_flags(req) & | ||
129 | CRYPTO_TFM_REQ_MAY_SLEEP, | ||
130 | areq_ctx->update_complete2, req); | ||
131 | |||
132 | err = crypto_ahash_update(ahreq); | ||
133 | if (err) | ||
134 | goto out; | ||
135 | |||
136 | ahash_request_set_crypt(ahreq, areq_ctx->tsg, ahreq->result, | ||
137 | areq_ctx->trailen); | ||
138 | ahash_request_set_callback(ahreq, aead_request_flags(req) & | ||
139 | CRYPTO_TFM_REQ_MAY_SLEEP, | ||
140 | areq_ctx->complete, req); | ||
141 | |||
142 | err = crypto_ahash_finup(ahreq); | ||
143 | if (err) | ||
144 | goto out; | ||
145 | |||
146 | scatterwalk_map_and_copy(ahreq->result, areq_ctx->sg, | ||
147 | areq_ctx->cryptlen, | ||
148 | crypto_aead_authsize(authenc_esn), 1); | ||
149 | |||
150 | out: | ||
151 | authenc_esn_request_complete(req, err); | ||
152 | } | ||
153 | |||
154 | static void authenc_esn_geniv_ahash_update_done2(struct crypto_async_request *areq, | ||
155 | int err) | ||
156 | { | ||
157 | struct aead_request *req = areq->data; | ||
158 | struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req); | ||
159 | struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn); | ||
160 | struct authenc_esn_request_ctx *areq_ctx = aead_request_ctx(req); | ||
161 | struct ahash_request *ahreq = (void *)(areq_ctx->tail + ctx->reqoff); | ||
162 | |||
163 | if (err) | ||
164 | goto out; | ||
165 | |||
166 | ahash_request_set_crypt(ahreq, areq_ctx->tsg, ahreq->result, | ||
167 | areq_ctx->trailen); | ||
168 | ahash_request_set_callback(ahreq, aead_request_flags(req) & | ||
169 | CRYPTO_TFM_REQ_MAY_SLEEP, | ||
170 | areq_ctx->complete, req); | ||
171 | |||
172 | err = crypto_ahash_finup(ahreq); | ||
173 | if (err) | ||
174 | goto out; | ||
175 | |||
176 | scatterwalk_map_and_copy(ahreq->result, areq_ctx->sg, | ||
177 | areq_ctx->cryptlen, | ||
178 | crypto_aead_authsize(authenc_esn), 1); | ||
179 | |||
180 | out: | ||
181 | authenc_esn_request_complete(req, err); | ||
182 | } | ||
183 | |||
184 | |||
185 | static void authenc_esn_geniv_ahash_done(struct crypto_async_request *areq, | ||
186 | int err) | ||
187 | { | ||
188 | struct aead_request *req = areq->data; | ||
189 | struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req); | ||
190 | struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn); | ||
191 | struct authenc_esn_request_ctx *areq_ctx = aead_request_ctx(req); | ||
192 | struct ahash_request *ahreq = (void *)(areq_ctx->tail + ctx->reqoff); | ||
193 | |||
194 | if (err) | ||
195 | goto out; | ||
196 | |||
197 | scatterwalk_map_and_copy(ahreq->result, areq_ctx->sg, | ||
198 | areq_ctx->cryptlen, | ||
199 | crypto_aead_authsize(authenc_esn), 1); | ||
200 | |||
201 | out: | ||
202 | aead_request_complete(req, err); | ||
203 | } | ||
204 | |||
205 | |||
206 | static void authenc_esn_verify_ahash_update_done(struct crypto_async_request *areq, | ||
207 | int err) | ||
208 | { | ||
209 | u8 *ihash; | ||
210 | unsigned int authsize; | ||
211 | struct ablkcipher_request *abreq; | ||
212 | struct aead_request *req = areq->data; | ||
213 | struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req); | ||
214 | struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn); | ||
215 | struct authenc_esn_request_ctx *areq_ctx = aead_request_ctx(req); | ||
216 | struct ahash_request *ahreq = (void *)(areq_ctx->tail + ctx->reqoff); | ||
217 | unsigned int cryptlen = req->cryptlen; | ||
218 | |||
219 | if (err) | ||
220 | goto out; | ||
221 | |||
222 | ahash_request_set_crypt(ahreq, areq_ctx->sg, ahreq->result, | ||
223 | areq_ctx->cryptlen); | ||
224 | |||
225 | ahash_request_set_callback(ahreq, | ||
226 | aead_request_flags(req) & | ||
227 | CRYPTO_TFM_REQ_MAY_SLEEP, | ||
228 | areq_ctx->update_complete2, req); | ||
229 | |||
230 | err = crypto_ahash_update(ahreq); | ||
231 | if (err) | ||
232 | goto out; | ||
233 | |||
234 | ahash_request_set_crypt(ahreq, areq_ctx->tsg, ahreq->result, | ||
235 | areq_ctx->trailen); | ||
236 | ahash_request_set_callback(ahreq, aead_request_flags(req) & | ||
237 | CRYPTO_TFM_REQ_MAY_SLEEP, | ||
238 | areq_ctx->complete, req); | ||
239 | |||
240 | err = crypto_ahash_finup(ahreq); | ||
241 | if (err) | ||
242 | goto out; | ||
243 | |||
244 | authsize = crypto_aead_authsize(authenc_esn); | ||
245 | cryptlen -= authsize; | ||
246 | ihash = ahreq->result + authsize; | ||
247 | scatterwalk_map_and_copy(ihash, areq_ctx->sg, areq_ctx->cryptlen, | ||
248 | authsize, 0); | ||
249 | |||
250 | err = memcmp(ihash, ahreq->result, authsize) ? -EBADMSG : 0; | ||
251 | if (err) | ||
252 | goto out; | ||
253 | |||
254 | abreq = aead_request_ctx(req); | ||
255 | ablkcipher_request_set_tfm(abreq, ctx->enc); | ||
256 | ablkcipher_request_set_callback(abreq, aead_request_flags(req), | ||
257 | req->base.complete, req->base.data); | ||
258 | ablkcipher_request_set_crypt(abreq, req->src, req->dst, | ||
259 | cryptlen, req->iv); | ||
260 | |||
261 | err = crypto_ablkcipher_decrypt(abreq); | ||
262 | |||
263 | out: | ||
264 | authenc_esn_request_complete(req, err); | ||
265 | } | ||
266 | |||
267 | static void authenc_esn_verify_ahash_update_done2(struct crypto_async_request *areq, | ||
268 | int err) | ||
269 | { | ||
270 | u8 *ihash; | ||
271 | unsigned int authsize; | ||
272 | struct ablkcipher_request *abreq; | ||
273 | struct aead_request *req = areq->data; | ||
274 | struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req); | ||
275 | struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn); | ||
276 | struct authenc_esn_request_ctx *areq_ctx = aead_request_ctx(req); | ||
277 | struct ahash_request *ahreq = (void *)(areq_ctx->tail + ctx->reqoff); | ||
278 | unsigned int cryptlen = req->cryptlen; | ||
279 | |||
280 | if (err) | ||
281 | goto out; | ||
282 | |||
283 | ahash_request_set_crypt(ahreq, areq_ctx->tsg, ahreq->result, | ||
284 | areq_ctx->trailen); | ||
285 | ahash_request_set_callback(ahreq, aead_request_flags(req) & | ||
286 | CRYPTO_TFM_REQ_MAY_SLEEP, | ||
287 | areq_ctx->complete, req); | ||
288 | |||
289 | err = crypto_ahash_finup(ahreq); | ||
290 | if (err) | ||
291 | goto out; | ||
292 | |||
293 | authsize = crypto_aead_authsize(authenc_esn); | ||
294 | cryptlen -= authsize; | ||
295 | ihash = ahreq->result + authsize; | ||
296 | scatterwalk_map_and_copy(ihash, areq_ctx->sg, areq_ctx->cryptlen, | ||
297 | authsize, 0); | ||
298 | |||
299 | err = memcmp(ihash, ahreq->result, authsize) ? -EBADMSG : 0; | ||
300 | if (err) | ||
301 | goto out; | ||
302 | |||
303 | abreq = aead_request_ctx(req); | ||
304 | ablkcipher_request_set_tfm(abreq, ctx->enc); | ||
305 | ablkcipher_request_set_callback(abreq, aead_request_flags(req), | ||
306 | req->base.complete, req->base.data); | ||
307 | ablkcipher_request_set_crypt(abreq, req->src, req->dst, | ||
308 | cryptlen, req->iv); | ||
309 | |||
310 | err = crypto_ablkcipher_decrypt(abreq); | ||
311 | |||
312 | out: | ||
313 | authenc_esn_request_complete(req, err); | ||
314 | } | ||
315 | |||
316 | |||
317 | static void authenc_esn_verify_ahash_done(struct crypto_async_request *areq, | ||
318 | int err) | ||
319 | { | ||
320 | u8 *ihash; | ||
321 | unsigned int authsize; | ||
322 | struct ablkcipher_request *abreq; | ||
323 | struct aead_request *req = areq->data; | ||
324 | struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req); | ||
325 | struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn); | ||
326 | struct authenc_esn_request_ctx *areq_ctx = aead_request_ctx(req); | ||
327 | struct ahash_request *ahreq = (void *)(areq_ctx->tail + ctx->reqoff); | ||
328 | unsigned int cryptlen = req->cryptlen; | ||
329 | |||
330 | if (err) | ||
331 | goto out; | ||
332 | |||
333 | authsize = crypto_aead_authsize(authenc_esn); | ||
334 | cryptlen -= authsize; | ||
335 | ihash = ahreq->result + authsize; | ||
336 | scatterwalk_map_and_copy(ihash, areq_ctx->sg, areq_ctx->cryptlen, | ||
337 | authsize, 0); | ||
338 | |||
339 | err = memcmp(ihash, ahreq->result, authsize) ? -EBADMSG : 0; | ||
340 | if (err) | ||
341 | goto out; | ||
342 | |||
343 | abreq = aead_request_ctx(req); | ||
344 | ablkcipher_request_set_tfm(abreq, ctx->enc); | ||
345 | ablkcipher_request_set_callback(abreq, aead_request_flags(req), | ||
346 | req->base.complete, req->base.data); | ||
347 | ablkcipher_request_set_crypt(abreq, req->src, req->dst, | ||
348 | cryptlen, req->iv); | ||
349 | |||
350 | err = crypto_ablkcipher_decrypt(abreq); | ||
351 | |||
352 | out: | ||
353 | authenc_esn_request_complete(req, err); | ||
354 | } | ||
355 | |||
356 | static u8 *crypto_authenc_esn_ahash(struct aead_request *req, | ||
357 | unsigned int flags) | ||
358 | { | ||
359 | struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req); | ||
360 | struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn); | ||
361 | struct crypto_ahash *auth = ctx->auth; | ||
362 | struct authenc_esn_request_ctx *areq_ctx = aead_request_ctx(req); | ||
363 | struct ahash_request *ahreq = (void *)(areq_ctx->tail + ctx->reqoff); | ||
364 | u8 *hash = areq_ctx->tail; | ||
365 | int err; | ||
366 | |||
367 | hash = (u8 *)ALIGN((unsigned long)hash + crypto_ahash_alignmask(auth), | ||
368 | crypto_ahash_alignmask(auth) + 1); | ||
369 | |||
370 | ahash_request_set_tfm(ahreq, auth); | ||
371 | |||
372 | err = crypto_ahash_init(ahreq); | ||
373 | if (err) | ||
374 | return ERR_PTR(err); | ||
375 | |||
376 | ahash_request_set_crypt(ahreq, areq_ctx->hsg, hash, areq_ctx->headlen); | ||
377 | ahash_request_set_callback(ahreq, aead_request_flags(req) & flags, | ||
378 | areq_ctx->update_complete, req); | ||
379 | |||
380 | err = crypto_ahash_update(ahreq); | ||
381 | if (err) | ||
382 | return ERR_PTR(err); | ||
383 | |||
384 | ahash_request_set_crypt(ahreq, areq_ctx->sg, hash, areq_ctx->cryptlen); | ||
385 | ahash_request_set_callback(ahreq, aead_request_flags(req) & flags, | ||
386 | areq_ctx->update_complete2, req); | ||
387 | |||
388 | err = crypto_ahash_update(ahreq); | ||
389 | if (err) | ||
390 | return ERR_PTR(err); | ||
391 | |||
392 | ahash_request_set_crypt(ahreq, areq_ctx->tsg, hash, | ||
393 | areq_ctx->trailen); | ||
394 | ahash_request_set_callback(ahreq, aead_request_flags(req) & flags, | ||
395 | areq_ctx->complete, req); | ||
396 | |||
397 | err = crypto_ahash_finup(ahreq); | ||
398 | if (err) | ||
399 | return ERR_PTR(err); | ||
400 | |||
401 | return hash; | ||
402 | } | ||
403 | |||
404 | static int crypto_authenc_esn_genicv(struct aead_request *req, u8 *iv, | ||
405 | unsigned int flags) | ||
406 | { | ||
407 | struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req); | ||
408 | struct authenc_esn_request_ctx *areq_ctx = aead_request_ctx(req); | ||
409 | struct scatterlist *dst = req->dst; | ||
410 | struct scatterlist *assoc = req->assoc; | ||
411 | struct scatterlist *cipher = areq_ctx->cipher; | ||
412 | struct scatterlist *hsg = areq_ctx->hsg; | ||
413 | struct scatterlist *tsg = areq_ctx->tsg; | ||
414 | struct scatterlist *assoc1; | ||
415 | struct scatterlist *assoc2; | ||
416 | unsigned int ivsize = crypto_aead_ivsize(authenc_esn); | ||
417 | unsigned int cryptlen = req->cryptlen; | ||
418 | struct page *dstp; | ||
419 | u8 *vdst; | ||
420 | u8 *hash; | ||
421 | |||
422 | dstp = sg_page(dst); | ||
423 | vdst = PageHighMem(dstp) ? NULL : page_address(dstp) + dst->offset; | ||
424 | |||
425 | if (ivsize) { | ||
426 | sg_init_table(cipher, 2); | ||
427 | sg_set_buf(cipher, iv, ivsize); | ||
428 | scatterwalk_crypto_chain(cipher, dst, vdst == iv + ivsize, 2); | ||
429 | dst = cipher; | ||
430 | cryptlen += ivsize; | ||
431 | } | ||
432 | |||
433 | if (sg_is_last(assoc)) | ||
434 | return -EINVAL; | ||
435 | |||
436 | assoc1 = assoc + 1; | ||
437 | if (sg_is_last(assoc1)) | ||
438 | return -EINVAL; | ||
439 | |||
440 | assoc2 = assoc + 2; | ||
441 | if (!sg_is_last(assoc2)) | ||
442 | return -EINVAL; | ||
443 | |||
444 | sg_init_table(hsg, 2); | ||
445 | sg_set_page(hsg, sg_page(assoc), assoc->length, assoc->offset); | ||
446 | sg_set_page(hsg + 1, sg_page(assoc2), assoc2->length, assoc2->offset); | ||
447 | |||
448 | sg_init_table(tsg, 1); | ||
449 | sg_set_page(tsg, sg_page(assoc1), assoc1->length, assoc1->offset); | ||
450 | |||
451 | areq_ctx->cryptlen = cryptlen; | ||
452 | areq_ctx->headlen = assoc->length + assoc2->length; | ||
453 | areq_ctx->trailen = assoc1->length; | ||
454 | areq_ctx->sg = dst; | ||
455 | |||
456 | areq_ctx->complete = authenc_esn_geniv_ahash_done; | ||
457 | areq_ctx->update_complete = authenc_esn_geniv_ahash_update_done; | ||
458 | areq_ctx->update_complete2 = authenc_esn_geniv_ahash_update_done2; | ||
459 | |||
460 | hash = crypto_authenc_esn_ahash(req, flags); | ||
461 | if (IS_ERR(hash)) | ||
462 | return PTR_ERR(hash); | ||
463 | |||
464 | scatterwalk_map_and_copy(hash, dst, cryptlen, | ||
465 | crypto_aead_authsize(authenc_esn), 1); | ||
466 | return 0; | ||
467 | } | ||
468 | |||
469 | |||
470 | static void crypto_authenc_esn_encrypt_done(struct crypto_async_request *req, | ||
471 | int err) | ||
472 | { | ||
473 | struct aead_request *areq = req->data; | ||
474 | |||
475 | if (!err) { | ||
476 | struct crypto_aead *authenc_esn = crypto_aead_reqtfm(areq); | ||
477 | struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn); | ||
478 | struct ablkcipher_request *abreq = aead_request_ctx(areq); | ||
479 | u8 *iv = (u8 *)(abreq + 1) + | ||
480 | crypto_ablkcipher_reqsize(ctx->enc); | ||
481 | |||
482 | err = crypto_authenc_esn_genicv(areq, iv, 0); | ||
483 | } | ||
484 | |||
485 | authenc_esn_request_complete(areq, err); | ||
486 | } | ||
487 | |||
488 | static int crypto_authenc_esn_encrypt(struct aead_request *req) | ||
489 | { | ||
490 | struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req); | ||
491 | struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn); | ||
492 | struct authenc_esn_request_ctx *areq_ctx = aead_request_ctx(req); | ||
493 | struct crypto_ablkcipher *enc = ctx->enc; | ||
494 | struct scatterlist *dst = req->dst; | ||
495 | unsigned int cryptlen = req->cryptlen; | ||
496 | struct ablkcipher_request *abreq = (void *)(areq_ctx->tail | ||
497 | + ctx->reqoff); | ||
498 | u8 *iv = (u8 *)abreq - crypto_ablkcipher_ivsize(enc); | ||
499 | int err; | ||
500 | |||
501 | ablkcipher_request_set_tfm(abreq, enc); | ||
502 | ablkcipher_request_set_callback(abreq, aead_request_flags(req), | ||
503 | crypto_authenc_esn_encrypt_done, req); | ||
504 | ablkcipher_request_set_crypt(abreq, req->src, dst, cryptlen, req->iv); | ||
505 | |||
506 | memcpy(iv, req->iv, crypto_aead_ivsize(authenc_esn)); | ||
507 | |||
508 | err = crypto_ablkcipher_encrypt(abreq); | ||
509 | if (err) | ||
510 | return err; | ||
511 | |||
512 | return crypto_authenc_esn_genicv(req, iv, CRYPTO_TFM_REQ_MAY_SLEEP); | ||
513 | } | ||
514 | |||
515 | static void crypto_authenc_esn_givencrypt_done(struct crypto_async_request *req, | ||
516 | int err) | ||
517 | { | ||
518 | struct aead_request *areq = req->data; | ||
519 | |||
520 | if (!err) { | ||
521 | struct skcipher_givcrypt_request *greq = aead_request_ctx(areq); | ||
522 | |||
523 | err = crypto_authenc_esn_genicv(areq, greq->giv, 0); | ||
524 | } | ||
525 | |||
526 | authenc_esn_request_complete(areq, err); | ||
527 | } | ||
528 | |||
529 | static int crypto_authenc_esn_givencrypt(struct aead_givcrypt_request *req) | ||
530 | { | ||
531 | struct crypto_aead *authenc_esn = aead_givcrypt_reqtfm(req); | ||
532 | struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn); | ||
533 | struct aead_request *areq = &req->areq; | ||
534 | struct skcipher_givcrypt_request *greq = aead_request_ctx(areq); | ||
535 | u8 *iv = req->giv; | ||
536 | int err; | ||
537 | |||
538 | skcipher_givcrypt_set_tfm(greq, ctx->enc); | ||
539 | skcipher_givcrypt_set_callback(greq, aead_request_flags(areq), | ||
540 | crypto_authenc_esn_givencrypt_done, areq); | ||
541 | skcipher_givcrypt_set_crypt(greq, areq->src, areq->dst, areq->cryptlen, | ||
542 | areq->iv); | ||
543 | skcipher_givcrypt_set_giv(greq, iv, req->seq); | ||
544 | |||
545 | err = crypto_skcipher_givencrypt(greq); | ||
546 | if (err) | ||
547 | return err; | ||
548 | |||
549 | return crypto_authenc_esn_genicv(areq, iv, CRYPTO_TFM_REQ_MAY_SLEEP); | ||
550 | } | ||
551 | |||
552 | static int crypto_authenc_esn_verify(struct aead_request *req) | ||
553 | { | ||
554 | struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req); | ||
555 | struct authenc_esn_request_ctx *areq_ctx = aead_request_ctx(req); | ||
556 | u8 *ohash; | ||
557 | u8 *ihash; | ||
558 | unsigned int authsize; | ||
559 | |||
560 | areq_ctx->complete = authenc_esn_verify_ahash_done; | ||
561 | areq_ctx->update_complete = authenc_esn_verify_ahash_update_done; | ||
562 | |||
563 | ohash = crypto_authenc_esn_ahash(req, CRYPTO_TFM_REQ_MAY_SLEEP); | ||
564 | if (IS_ERR(ohash)) | ||
565 | return PTR_ERR(ohash); | ||
566 | |||
567 | authsize = crypto_aead_authsize(authenc_esn); | ||
568 | ihash = ohash + authsize; | ||
569 | scatterwalk_map_and_copy(ihash, areq_ctx->sg, areq_ctx->cryptlen, | ||
570 | authsize, 0); | ||
571 | return memcmp(ihash, ohash, authsize) ? -EBADMSG : 0; | ||
572 | } | ||
573 | |||
574 | static int crypto_authenc_esn_iverify(struct aead_request *req, u8 *iv, | ||
575 | unsigned int cryptlen) | ||
576 | { | ||
577 | struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req); | ||
578 | struct authenc_esn_request_ctx *areq_ctx = aead_request_ctx(req); | ||
579 | struct scatterlist *src = req->src; | ||
580 | struct scatterlist *assoc = req->assoc; | ||
581 | struct scatterlist *cipher = areq_ctx->cipher; | ||
582 | struct scatterlist *hsg = areq_ctx->hsg; | ||
583 | struct scatterlist *tsg = areq_ctx->tsg; | ||
584 | struct scatterlist *assoc1; | ||
585 | struct scatterlist *assoc2; | ||
586 | unsigned int ivsize = crypto_aead_ivsize(authenc_esn); | ||
587 | struct page *srcp; | ||
588 | u8 *vsrc; | ||
589 | |||
590 | srcp = sg_page(src); | ||
591 | vsrc = PageHighMem(srcp) ? NULL : page_address(srcp) + src->offset; | ||
592 | |||
593 | if (ivsize) { | ||
594 | sg_init_table(cipher, 2); | ||
595 | sg_set_buf(cipher, iv, ivsize); | ||
596 | scatterwalk_crypto_chain(cipher, src, vsrc == iv + ivsize, 2); | ||
597 | src = cipher; | ||
598 | cryptlen += ivsize; | ||
599 | } | ||
600 | |||
601 | if (sg_is_last(assoc)) | ||
602 | return -EINVAL; | ||
603 | |||
604 | assoc1 = assoc + 1; | ||
605 | if (sg_is_last(assoc1)) | ||
606 | return -EINVAL; | ||
607 | |||
608 | assoc2 = assoc + 2; | ||
609 | if (!sg_is_last(assoc2)) | ||
610 | return -EINVAL; | ||
611 | |||
612 | sg_init_table(hsg, 2); | ||
613 | sg_set_page(hsg, sg_page(assoc), assoc->length, assoc->offset); | ||
614 | sg_set_page(hsg + 1, sg_page(assoc2), assoc2->length, assoc2->offset); | ||
615 | |||
616 | sg_init_table(tsg, 1); | ||
617 | sg_set_page(tsg, sg_page(assoc1), assoc1->length, assoc1->offset); | ||
618 | |||
619 | areq_ctx->cryptlen = cryptlen; | ||
620 | areq_ctx->headlen = assoc->length + assoc2->length; | ||
621 | areq_ctx->trailen = assoc1->length; | ||
622 | areq_ctx->sg = src; | ||
623 | |||
624 | areq_ctx->complete = authenc_esn_verify_ahash_done; | ||
625 | areq_ctx->update_complete = authenc_esn_verify_ahash_update_done; | ||
626 | areq_ctx->update_complete2 = authenc_esn_verify_ahash_update_done2; | ||
627 | |||
628 | return crypto_authenc_esn_verify(req); | ||
629 | } | ||
630 | |||
631 | static int crypto_authenc_esn_decrypt(struct aead_request *req) | ||
632 | { | ||
633 | struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req); | ||
634 | struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn); | ||
635 | struct ablkcipher_request *abreq = aead_request_ctx(req); | ||
636 | unsigned int cryptlen = req->cryptlen; | ||
637 | unsigned int authsize = crypto_aead_authsize(authenc_esn); | ||
638 | u8 *iv = req->iv; | ||
639 | int err; | ||
640 | |||
641 | if (cryptlen < authsize) | ||
642 | return -EINVAL; | ||
643 | cryptlen -= authsize; | ||
644 | |||
645 | err = crypto_authenc_esn_iverify(req, iv, cryptlen); | ||
646 | if (err) | ||
647 | return err; | ||
648 | |||
649 | ablkcipher_request_set_tfm(abreq, ctx->enc); | ||
650 | ablkcipher_request_set_callback(abreq, aead_request_flags(req), | ||
651 | req->base.complete, req->base.data); | ||
652 | ablkcipher_request_set_crypt(abreq, req->src, req->dst, cryptlen, iv); | ||
653 | |||
654 | return crypto_ablkcipher_decrypt(abreq); | ||
655 | } | ||
656 | |||
657 | static int crypto_authenc_esn_init_tfm(struct crypto_tfm *tfm) | ||
658 | { | ||
659 | struct crypto_instance *inst = crypto_tfm_alg_instance(tfm); | ||
660 | struct authenc_esn_instance_ctx *ictx = crypto_instance_ctx(inst); | ||
661 | struct crypto_authenc_esn_ctx *ctx = crypto_tfm_ctx(tfm); | ||
662 | struct crypto_ahash *auth; | ||
663 | struct crypto_ablkcipher *enc; | ||
664 | int err; | ||
665 | |||
666 | auth = crypto_spawn_ahash(&ictx->auth); | ||
667 | if (IS_ERR(auth)) | ||
668 | return PTR_ERR(auth); | ||
669 | |||
670 | enc = crypto_spawn_skcipher(&ictx->enc); | ||
671 | err = PTR_ERR(enc); | ||
672 | if (IS_ERR(enc)) | ||
673 | goto err_free_ahash; | ||
674 | |||
675 | ctx->auth = auth; | ||
676 | ctx->enc = enc; | ||
677 | |||
678 | ctx->reqoff = ALIGN(2 * crypto_ahash_digestsize(auth) + | ||
679 | crypto_ahash_alignmask(auth), | ||
680 | crypto_ahash_alignmask(auth) + 1) + | ||
681 | crypto_ablkcipher_ivsize(enc); | ||
682 | |||
683 | tfm->crt_aead.reqsize = sizeof(struct authenc_esn_request_ctx) + | ||
684 | ctx->reqoff + | ||
685 | max_t(unsigned int, | ||
686 | crypto_ahash_reqsize(auth) + | ||
687 | sizeof(struct ahash_request), | ||
688 | sizeof(struct skcipher_givcrypt_request) + | ||
689 | crypto_ablkcipher_reqsize(enc)); | ||
690 | |||
691 | return 0; | ||
692 | |||
693 | err_free_ahash: | ||
694 | crypto_free_ahash(auth); | ||
695 | return err; | ||
696 | } | ||
697 | |||
698 | static void crypto_authenc_esn_exit_tfm(struct crypto_tfm *tfm) | ||
699 | { | ||
700 | struct crypto_authenc_esn_ctx *ctx = crypto_tfm_ctx(tfm); | ||
701 | |||
702 | crypto_free_ahash(ctx->auth); | ||
703 | crypto_free_ablkcipher(ctx->enc); | ||
704 | } | ||
705 | |||
706 | static struct crypto_instance *crypto_authenc_esn_alloc(struct rtattr **tb) | ||
707 | { | ||
708 | struct crypto_attr_type *algt; | ||
709 | struct crypto_instance *inst; | ||
710 | struct hash_alg_common *auth; | ||
711 | struct crypto_alg *auth_base; | ||
712 | struct crypto_alg *enc; | ||
713 | struct authenc_esn_instance_ctx *ctx; | ||
714 | const char *enc_name; | ||
715 | int err; | ||
716 | |||
717 | algt = crypto_get_attr_type(tb); | ||
718 | err = PTR_ERR(algt); | ||
719 | if (IS_ERR(algt)) | ||
720 | return ERR_PTR(err); | ||
721 | |||
722 | if ((algt->type ^ CRYPTO_ALG_TYPE_AEAD) & algt->mask) | ||
723 | return ERR_PTR(-EINVAL); | ||
724 | |||
725 | auth = ahash_attr_alg(tb[1], CRYPTO_ALG_TYPE_HASH, | ||
726 | CRYPTO_ALG_TYPE_AHASH_MASK); | ||
727 | if (IS_ERR(auth)) | ||
728 | return ERR_CAST(auth); | ||
729 | |||
730 | auth_base = &auth->base; | ||
731 | |||
732 | enc_name = crypto_attr_alg_name(tb[2]); | ||
733 | err = PTR_ERR(enc_name); | ||
734 | if (IS_ERR(enc_name)) | ||
735 | goto out_put_auth; | ||
736 | |||
737 | inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL); | ||
738 | err = -ENOMEM; | ||
739 | if (!inst) | ||
740 | goto out_put_auth; | ||
741 | |||
742 | ctx = crypto_instance_ctx(inst); | ||
743 | |||
744 | err = crypto_init_ahash_spawn(&ctx->auth, auth, inst); | ||
745 | if (err) | ||
746 | goto err_free_inst; | ||
747 | |||
748 | crypto_set_skcipher_spawn(&ctx->enc, inst); | ||
749 | err = crypto_grab_skcipher(&ctx->enc, enc_name, 0, | ||
750 | crypto_requires_sync(algt->type, | ||
751 | algt->mask)); | ||
752 | if (err) | ||
753 | goto err_drop_auth; | ||
754 | |||
755 | enc = crypto_skcipher_spawn_alg(&ctx->enc); | ||
756 | |||
757 | err = -ENAMETOOLONG; | ||
758 | if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME, | ||
759 | "authencesn(%s,%s)", auth_base->cra_name, enc->cra_name) >= | ||
760 | CRYPTO_MAX_ALG_NAME) | ||
761 | goto err_drop_enc; | ||
762 | |||
763 | if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME, | ||
764 | "authencesn(%s,%s)", auth_base->cra_driver_name, | ||
765 | enc->cra_driver_name) >= CRYPTO_MAX_ALG_NAME) | ||
766 | goto err_drop_enc; | ||
767 | |||
768 | inst->alg.cra_flags = CRYPTO_ALG_TYPE_AEAD; | ||
769 | inst->alg.cra_flags |= enc->cra_flags & CRYPTO_ALG_ASYNC; | ||
770 | inst->alg.cra_priority = enc->cra_priority * | ||
771 | 10 + auth_base->cra_priority; | ||
772 | inst->alg.cra_blocksize = enc->cra_blocksize; | ||
773 | inst->alg.cra_alignmask = auth_base->cra_alignmask | enc->cra_alignmask; | ||
774 | inst->alg.cra_type = &crypto_aead_type; | ||
775 | |||
776 | inst->alg.cra_aead.ivsize = enc->cra_ablkcipher.ivsize; | ||
777 | inst->alg.cra_aead.maxauthsize = auth->digestsize; | ||
778 | |||
779 | inst->alg.cra_ctxsize = sizeof(struct crypto_authenc_esn_ctx); | ||
780 | |||
781 | inst->alg.cra_init = crypto_authenc_esn_init_tfm; | ||
782 | inst->alg.cra_exit = crypto_authenc_esn_exit_tfm; | ||
783 | |||
784 | inst->alg.cra_aead.setkey = crypto_authenc_esn_setkey; | ||
785 | inst->alg.cra_aead.encrypt = crypto_authenc_esn_encrypt; | ||
786 | inst->alg.cra_aead.decrypt = crypto_authenc_esn_decrypt; | ||
787 | inst->alg.cra_aead.givencrypt = crypto_authenc_esn_givencrypt; | ||
788 | |||
789 | out: | ||
790 | crypto_mod_put(auth_base); | ||
791 | return inst; | ||
792 | |||
793 | err_drop_enc: | ||
794 | crypto_drop_skcipher(&ctx->enc); | ||
795 | err_drop_auth: | ||
796 | crypto_drop_ahash(&ctx->auth); | ||
797 | err_free_inst: | ||
798 | kfree(inst); | ||
799 | out_put_auth: | ||
800 | inst = ERR_PTR(err); | ||
801 | goto out; | ||
802 | } | ||
803 | |||
804 | static void crypto_authenc_esn_free(struct crypto_instance *inst) | ||
805 | { | ||
806 | struct authenc_esn_instance_ctx *ctx = crypto_instance_ctx(inst); | ||
807 | |||
808 | crypto_drop_skcipher(&ctx->enc); | ||
809 | crypto_drop_ahash(&ctx->auth); | ||
810 | kfree(inst); | ||
811 | } | ||
812 | |||
813 | static struct crypto_template crypto_authenc_esn_tmpl = { | ||
814 | .name = "authencesn", | ||
815 | .alloc = crypto_authenc_esn_alloc, | ||
816 | .free = crypto_authenc_esn_free, | ||
817 | .module = THIS_MODULE, | ||
818 | }; | ||
819 | |||
820 | static int __init crypto_authenc_esn_module_init(void) | ||
821 | { | ||
822 | return crypto_register_template(&crypto_authenc_esn_tmpl); | ||
823 | } | ||
824 | |||
825 | static void __exit crypto_authenc_esn_module_exit(void) | ||
826 | { | ||
827 | crypto_unregister_template(&crypto_authenc_esn_tmpl); | ||
828 | } | ||
829 | |||
830 | module_init(crypto_authenc_esn_module_init); | ||
831 | module_exit(crypto_authenc_esn_module_exit); | ||
832 | |||
833 | MODULE_LICENSE("GPL"); | ||
834 | MODULE_AUTHOR("Steffen Klassert <steffen.klassert@secunet.com>"); | ||
835 | MODULE_DESCRIPTION("AEAD wrapper for IPsec with extended sequence numbers"); | ||