blob: 86260937b0f32a59abf7298695ec880a10f43a86 [file] [log] [blame]
Martin Willi71ebc4d2015-06-01 13:44:00 +02001/*
2 * ChaCha20-Poly1305 AEAD, RFC7539
3 *
4 * Copyright (C) 2015 Martin Willi
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12#include <crypto/internal/aead.h>
13#include <crypto/internal/hash.h>
14#include <crypto/internal/skcipher.h>
15#include <crypto/scatterwalk.h>
16#include <linux/err.h>
17#include <linux/init.h>
18#include <linux/kernel.h>
19#include <linux/module.h>
20
21#include "internal.h"
22
23#define POLY1305_BLOCK_SIZE 16
24#define POLY1305_DIGEST_SIZE 16
25#define POLY1305_KEY_SIZE 32
26#define CHACHA20_KEY_SIZE 32
27#define CHACHA20_IV_SIZE 16
28#define CHACHAPOLY_IV_SIZE 12
29
30struct chachapoly_instance_ctx {
31 struct crypto_skcipher_spawn chacha;
32 struct crypto_ahash_spawn poly;
33 unsigned int saltlen;
34};
35
36struct chachapoly_ctx {
37 struct crypto_ablkcipher *chacha;
38 struct crypto_ahash *poly;
39 /* key bytes we use for the ChaCha20 IV */
40 unsigned int saltlen;
41 u8 salt[];
42};
43
44struct poly_req {
45 /* zero byte padding for AD/ciphertext, as needed */
46 u8 pad[POLY1305_BLOCK_SIZE];
47 /* tail data with AD/ciphertext lengths */
48 struct {
49 __le64 assoclen;
50 __le64 cryptlen;
51 } tail;
52 struct scatterlist src[1];
53 struct ahash_request req; /* must be last member */
54};
55
56struct chacha_req {
Martin Willi71ebc4d2015-06-01 13:44:00 +020057 u8 iv[CHACHA20_IV_SIZE];
58 struct scatterlist src[1];
59 struct ablkcipher_request req; /* must be last member */
60};
61
62struct chachapoly_req_ctx {
Herbert Xu74790922015-07-16 12:35:08 +080063 struct scatterlist src[2];
64 struct scatterlist dst[2];
Martin Willic2b7b20a2015-06-16 11:34:16 +020065 /* the key we generate for Poly1305 using Chacha20 */
66 u8 key[POLY1305_KEY_SIZE];
Martin Willi71ebc4d2015-06-01 13:44:00 +020067 /* calculated Poly1305 tag */
68 u8 tag[POLY1305_DIGEST_SIZE];
69 /* length of data to en/decrypt, without ICV */
70 unsigned int cryptlen;
Herbert Xu74790922015-07-16 12:35:08 +080071 /* Actual AD, excluding IV */
72 unsigned int assoclen;
Martin Willi71ebc4d2015-06-01 13:44:00 +020073 union {
74 struct poly_req poly;
75 struct chacha_req chacha;
76 } u;
77};
78
79static inline void async_done_continue(struct aead_request *req, int err,
80 int (*cont)(struct aead_request *))
81{
82 if (!err)
83 err = cont(req);
84
85 if (err != -EINPROGRESS && err != -EBUSY)
86 aead_request_complete(req, err);
87}
88
89static void chacha_iv(u8 *iv, struct aead_request *req, u32 icb)
90{
91 struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
92 __le32 leicb = cpu_to_le32(icb);
93
94 memcpy(iv, &leicb, sizeof(leicb));
95 memcpy(iv + sizeof(leicb), ctx->salt, ctx->saltlen);
96 memcpy(iv + sizeof(leicb) + ctx->saltlen, req->iv,
97 CHACHA20_IV_SIZE - sizeof(leicb) - ctx->saltlen);
98}
99
100static int poly_verify_tag(struct aead_request *req)
101{
102 struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
103 u8 tag[sizeof(rctx->tag)];
104
Herbert Xu74790922015-07-16 12:35:08 +0800105 scatterwalk_map_and_copy(tag, req->src,
106 req->assoclen + rctx->cryptlen,
107 sizeof(tag), 0);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200108 if (crypto_memneq(tag, rctx->tag, sizeof(tag)))
109 return -EBADMSG;
110 return 0;
111}
112
113static int poly_copy_tag(struct aead_request *req)
114{
115 struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
116
Herbert Xu74790922015-07-16 12:35:08 +0800117 scatterwalk_map_and_copy(rctx->tag, req->dst,
118 req->assoclen + rctx->cryptlen,
Martin Willi71ebc4d2015-06-01 13:44:00 +0200119 sizeof(rctx->tag), 1);
120 return 0;
121}
122
123static void chacha_decrypt_done(struct crypto_async_request *areq, int err)
124{
125 async_done_continue(areq->data, err, poly_verify_tag);
126}
127
128static int chacha_decrypt(struct aead_request *req)
129{
130 struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
131 struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
132 struct chacha_req *creq = &rctx->u.chacha;
Herbert Xu74790922015-07-16 12:35:08 +0800133 struct scatterlist *src, *dst;
Martin Willi71ebc4d2015-06-01 13:44:00 +0200134 int err;
135
136 chacha_iv(creq->iv, req, 1);
137
Herbert Xu74790922015-07-16 12:35:08 +0800138 sg_init_table(rctx->src, 2);
139 src = scatterwalk_ffwd(rctx->src, req->src, req->assoclen);
140 dst = src;
141
142 if (req->src != req->dst) {
143 sg_init_table(rctx->dst, 2);
144 dst = scatterwalk_ffwd(rctx->dst, req->dst, req->assoclen);
145 }
146
Martin Willi71ebc4d2015-06-01 13:44:00 +0200147 ablkcipher_request_set_callback(&creq->req, aead_request_flags(req),
148 chacha_decrypt_done, req);
149 ablkcipher_request_set_tfm(&creq->req, ctx->chacha);
Herbert Xu74790922015-07-16 12:35:08 +0800150 ablkcipher_request_set_crypt(&creq->req, src, dst,
Martin Willi71ebc4d2015-06-01 13:44:00 +0200151 rctx->cryptlen, creq->iv);
152 err = crypto_ablkcipher_decrypt(&creq->req);
153 if (err)
154 return err;
155
156 return poly_verify_tag(req);
157}
158
159static int poly_tail_continue(struct aead_request *req)
160{
161 struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
162
163 if (rctx->cryptlen == req->cryptlen) /* encrypting */
164 return poly_copy_tag(req);
165
166 return chacha_decrypt(req);
167}
168
169static void poly_tail_done(struct crypto_async_request *areq, int err)
170{
171 async_done_continue(areq->data, err, poly_tail_continue);
172}
173
174static int poly_tail(struct aead_request *req)
175{
Herbert Xu74790922015-07-16 12:35:08 +0800176 struct crypto_aead *tfm = crypto_aead_reqtfm(req);
177 struct chachapoly_ctx *ctx = crypto_aead_ctx(tfm);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200178 struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
179 struct poly_req *preq = &rctx->u.poly;
180 __le64 len;
181 int err;
182
183 sg_init_table(preq->src, 1);
Herbert Xu74790922015-07-16 12:35:08 +0800184 len = cpu_to_le64(rctx->assoclen);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200185 memcpy(&preq->tail.assoclen, &len, sizeof(len));
186 len = cpu_to_le64(rctx->cryptlen);
187 memcpy(&preq->tail.cryptlen, &len, sizeof(len));
188 sg_set_buf(preq->src, &preq->tail, sizeof(preq->tail));
189
190 ahash_request_set_callback(&preq->req, aead_request_flags(req),
191 poly_tail_done, req);
192 ahash_request_set_tfm(&preq->req, ctx->poly);
193 ahash_request_set_crypt(&preq->req, preq->src,
194 rctx->tag, sizeof(preq->tail));
195
196 err = crypto_ahash_finup(&preq->req);
197 if (err)
198 return err;
199
200 return poly_tail_continue(req);
201}
202
203static void poly_cipherpad_done(struct crypto_async_request *areq, int err)
204{
205 async_done_continue(areq->data, err, poly_tail);
206}
207
208static int poly_cipherpad(struct aead_request *req)
209{
210 struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
211 struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
212 struct poly_req *preq = &rctx->u.poly;
213 unsigned int padlen, bs = POLY1305_BLOCK_SIZE;
214 int err;
215
216 padlen = (bs - (rctx->cryptlen % bs)) % bs;
217 memset(preq->pad, 0, sizeof(preq->pad));
218 sg_init_table(preq->src, 1);
219 sg_set_buf(preq->src, &preq->pad, padlen);
220
221 ahash_request_set_callback(&preq->req, aead_request_flags(req),
222 poly_cipherpad_done, req);
223 ahash_request_set_tfm(&preq->req, ctx->poly);
224 ahash_request_set_crypt(&preq->req, preq->src, NULL, padlen);
225
226 err = crypto_ahash_update(&preq->req);
227 if (err)
228 return err;
229
230 return poly_tail(req);
231}
232
233static void poly_cipher_done(struct crypto_async_request *areq, int err)
234{
235 async_done_continue(areq->data, err, poly_cipherpad);
236}
237
238static int poly_cipher(struct aead_request *req)
239{
240 struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
241 struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
242 struct poly_req *preq = &rctx->u.poly;
243 struct scatterlist *crypt = req->src;
244 int err;
245
246 if (rctx->cryptlen == req->cryptlen) /* encrypting */
247 crypt = req->dst;
248
Herbert Xu74790922015-07-16 12:35:08 +0800249 sg_init_table(rctx->src, 2);
250 crypt = scatterwalk_ffwd(rctx->src, crypt, req->assoclen);
251
Martin Willi71ebc4d2015-06-01 13:44:00 +0200252 ahash_request_set_callback(&preq->req, aead_request_flags(req),
253 poly_cipher_done, req);
254 ahash_request_set_tfm(&preq->req, ctx->poly);
255 ahash_request_set_crypt(&preq->req, crypt, NULL, rctx->cryptlen);
256
257 err = crypto_ahash_update(&preq->req);
258 if (err)
259 return err;
260
261 return poly_cipherpad(req);
262}
263
264static void poly_adpad_done(struct crypto_async_request *areq, int err)
265{
266 async_done_continue(areq->data, err, poly_cipher);
267}
268
269static int poly_adpad(struct aead_request *req)
270{
271 struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
272 struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
273 struct poly_req *preq = &rctx->u.poly;
274 unsigned int padlen, bs = POLY1305_BLOCK_SIZE;
275 int err;
276
Herbert Xu74790922015-07-16 12:35:08 +0800277 padlen = (bs - (rctx->assoclen % bs)) % bs;
Martin Willi71ebc4d2015-06-01 13:44:00 +0200278 memset(preq->pad, 0, sizeof(preq->pad));
279 sg_init_table(preq->src, 1);
280 sg_set_buf(preq->src, preq->pad, padlen);
281
282 ahash_request_set_callback(&preq->req, aead_request_flags(req),
283 poly_adpad_done, req);
284 ahash_request_set_tfm(&preq->req, ctx->poly);
285 ahash_request_set_crypt(&preq->req, preq->src, NULL, padlen);
286
287 err = crypto_ahash_update(&preq->req);
288 if (err)
289 return err;
290
291 return poly_cipher(req);
292}
293
294static void poly_ad_done(struct crypto_async_request *areq, int err)
295{
296 async_done_continue(areq->data, err, poly_adpad);
297}
298
299static int poly_ad(struct aead_request *req)
300{
301 struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
302 struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
303 struct poly_req *preq = &rctx->u.poly;
304 int err;
305
306 ahash_request_set_callback(&preq->req, aead_request_flags(req),
307 poly_ad_done, req);
308 ahash_request_set_tfm(&preq->req, ctx->poly);
Herbert Xu74790922015-07-16 12:35:08 +0800309 ahash_request_set_crypt(&preq->req, req->src, NULL, rctx->assoclen);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200310
311 err = crypto_ahash_update(&preq->req);
312 if (err)
313 return err;
314
315 return poly_adpad(req);
316}
317
Martin Willic2b7b20a2015-06-16 11:34:16 +0200318static void poly_setkey_done(struct crypto_async_request *areq, int err)
Martin Willi71ebc4d2015-06-01 13:44:00 +0200319{
320 async_done_continue(areq->data, err, poly_ad);
321}
322
Martin Willic2b7b20a2015-06-16 11:34:16 +0200323static int poly_setkey(struct aead_request *req)
324{
325 struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
326 struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
327 struct poly_req *preq = &rctx->u.poly;
328 int err;
329
330 sg_init_table(preq->src, 1);
331 sg_set_buf(preq->src, rctx->key, sizeof(rctx->key));
332
333 ahash_request_set_callback(&preq->req, aead_request_flags(req),
334 poly_setkey_done, req);
335 ahash_request_set_tfm(&preq->req, ctx->poly);
336 ahash_request_set_crypt(&preq->req, preq->src, NULL, sizeof(rctx->key));
337
338 err = crypto_ahash_update(&preq->req);
339 if (err)
340 return err;
341
342 return poly_ad(req);
343}
344
345static void poly_init_done(struct crypto_async_request *areq, int err)
346{
347 async_done_continue(areq->data, err, poly_setkey);
348}
349
Martin Willi71ebc4d2015-06-01 13:44:00 +0200350static int poly_init(struct aead_request *req)
351{
352 struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
353 struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
354 struct poly_req *preq = &rctx->u.poly;
355 int err;
356
357 ahash_request_set_callback(&preq->req, aead_request_flags(req),
358 poly_init_done, req);
359 ahash_request_set_tfm(&preq->req, ctx->poly);
360
361 err = crypto_ahash_init(&preq->req);
362 if (err)
363 return err;
364
Martin Willic2b7b20a2015-06-16 11:34:16 +0200365 return poly_setkey(req);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200366}
367
368static void poly_genkey_done(struct crypto_async_request *areq, int err)
369{
Martin Willic2b7b20a2015-06-16 11:34:16 +0200370 async_done_continue(areq->data, err, poly_init);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200371}
372
373static int poly_genkey(struct aead_request *req)
374{
Herbert Xu74790922015-07-16 12:35:08 +0800375 struct crypto_aead *tfm = crypto_aead_reqtfm(req);
376 struct chachapoly_ctx *ctx = crypto_aead_ctx(tfm);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200377 struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
378 struct chacha_req *creq = &rctx->u.chacha;
379 int err;
380
Herbert Xu74790922015-07-16 12:35:08 +0800381 rctx->assoclen = req->assoclen;
382
383 if (crypto_aead_ivsize(tfm) == 8) {
384 if (rctx->assoclen < 8)
385 return -EINVAL;
386 rctx->assoclen -= 8;
387 }
388
Martin Willi71ebc4d2015-06-01 13:44:00 +0200389 sg_init_table(creq->src, 1);
Martin Willic2b7b20a2015-06-16 11:34:16 +0200390 memset(rctx->key, 0, sizeof(rctx->key));
391 sg_set_buf(creq->src, rctx->key, sizeof(rctx->key));
Martin Willi71ebc4d2015-06-01 13:44:00 +0200392
393 chacha_iv(creq->iv, req, 0);
394
395 ablkcipher_request_set_callback(&creq->req, aead_request_flags(req),
396 poly_genkey_done, req);
397 ablkcipher_request_set_tfm(&creq->req, ctx->chacha);
398 ablkcipher_request_set_crypt(&creq->req, creq->src, creq->src,
399 POLY1305_KEY_SIZE, creq->iv);
400
401 err = crypto_ablkcipher_decrypt(&creq->req);
402 if (err)
403 return err;
404
Martin Willic2b7b20a2015-06-16 11:34:16 +0200405 return poly_init(req);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200406}
407
408static void chacha_encrypt_done(struct crypto_async_request *areq, int err)
409{
410 async_done_continue(areq->data, err, poly_genkey);
411}
412
413static int chacha_encrypt(struct aead_request *req)
414{
415 struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
416 struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
417 struct chacha_req *creq = &rctx->u.chacha;
Herbert Xu74790922015-07-16 12:35:08 +0800418 struct scatterlist *src, *dst;
Martin Willi71ebc4d2015-06-01 13:44:00 +0200419 int err;
420
421 chacha_iv(creq->iv, req, 1);
422
Herbert Xu74790922015-07-16 12:35:08 +0800423 sg_init_table(rctx->src, 2);
424 src = scatterwalk_ffwd(rctx->src, req->src, req->assoclen);
425 dst = src;
426
427 if (req->src != req->dst) {
428 sg_init_table(rctx->dst, 2);
429 dst = scatterwalk_ffwd(rctx->dst, req->dst, req->assoclen);
430 }
431
Martin Willi71ebc4d2015-06-01 13:44:00 +0200432 ablkcipher_request_set_callback(&creq->req, aead_request_flags(req),
433 chacha_encrypt_done, req);
434 ablkcipher_request_set_tfm(&creq->req, ctx->chacha);
Herbert Xu74790922015-07-16 12:35:08 +0800435 ablkcipher_request_set_crypt(&creq->req, src, dst,
Martin Willi71ebc4d2015-06-01 13:44:00 +0200436 req->cryptlen, creq->iv);
437 err = crypto_ablkcipher_encrypt(&creq->req);
438 if (err)
439 return err;
440
441 return poly_genkey(req);
442}
443
444static int chachapoly_encrypt(struct aead_request *req)
445{
446 struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
447
448 rctx->cryptlen = req->cryptlen;
449
450 /* encrypt call chain:
451 * - chacha_encrypt/done()
Martin Willic2b7b20a2015-06-16 11:34:16 +0200452 * - poly_genkey/done()
Martin Willi71ebc4d2015-06-01 13:44:00 +0200453 * - poly_init/done()
Martin Willic2b7b20a2015-06-16 11:34:16 +0200454 * - poly_setkey/done()
Martin Willi71ebc4d2015-06-01 13:44:00 +0200455 * - poly_ad/done()
456 * - poly_adpad/done()
457 * - poly_cipher/done()
458 * - poly_cipherpad/done()
459 * - poly_tail/done/continue()
460 * - poly_copy_tag()
461 */
462 return chacha_encrypt(req);
463}
464
465static int chachapoly_decrypt(struct aead_request *req)
466{
467 struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
468
Martin Willi71ebc4d2015-06-01 13:44:00 +0200469 rctx->cryptlen = req->cryptlen - POLY1305_DIGEST_SIZE;
470
471 /* decrypt call chain:
Martin Willic2b7b20a2015-06-16 11:34:16 +0200472 * - poly_genkey/done()
Martin Willi71ebc4d2015-06-01 13:44:00 +0200473 * - poly_init/done()
Martin Willic2b7b20a2015-06-16 11:34:16 +0200474 * - poly_setkey/done()
Martin Willi71ebc4d2015-06-01 13:44:00 +0200475 * - poly_ad/done()
476 * - poly_adpad/done()
477 * - poly_cipher/done()
478 * - poly_cipherpad/done()
479 * - poly_tail/done/continue()
480 * - chacha_decrypt/done()
481 * - poly_verify_tag()
482 */
483 return poly_genkey(req);
484}
485
486static int chachapoly_setkey(struct crypto_aead *aead, const u8 *key,
487 unsigned int keylen)
488{
489 struct chachapoly_ctx *ctx = crypto_aead_ctx(aead);
490 int err;
491
492 if (keylen != ctx->saltlen + CHACHA20_KEY_SIZE)
493 return -EINVAL;
494
495 keylen -= ctx->saltlen;
496 memcpy(ctx->salt, key + keylen, ctx->saltlen);
497
498 crypto_ablkcipher_clear_flags(ctx->chacha, CRYPTO_TFM_REQ_MASK);
499 crypto_ablkcipher_set_flags(ctx->chacha, crypto_aead_get_flags(aead) &
500 CRYPTO_TFM_REQ_MASK);
501
502 err = crypto_ablkcipher_setkey(ctx->chacha, key, keylen);
503 crypto_aead_set_flags(aead, crypto_ablkcipher_get_flags(ctx->chacha) &
504 CRYPTO_TFM_RES_MASK);
505 return err;
506}
507
508static int chachapoly_setauthsize(struct crypto_aead *tfm,
509 unsigned int authsize)
510{
511 if (authsize != POLY1305_DIGEST_SIZE)
512 return -EINVAL;
513
514 return 0;
515}
516
Herbert Xu74790922015-07-16 12:35:08 +0800517static int chachapoly_init(struct crypto_aead *tfm)
Martin Willi71ebc4d2015-06-01 13:44:00 +0200518{
Herbert Xu74790922015-07-16 12:35:08 +0800519 struct aead_instance *inst = aead_alg_instance(tfm);
520 struct chachapoly_instance_ctx *ictx = aead_instance_ctx(inst);
521 struct chachapoly_ctx *ctx = crypto_aead_ctx(tfm);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200522 struct crypto_ablkcipher *chacha;
523 struct crypto_ahash *poly;
524 unsigned long align;
525
526 poly = crypto_spawn_ahash(&ictx->poly);
527 if (IS_ERR(poly))
528 return PTR_ERR(poly);
529
530 chacha = crypto_spawn_skcipher(&ictx->chacha);
531 if (IS_ERR(chacha)) {
532 crypto_free_ahash(poly);
533 return PTR_ERR(chacha);
534 }
535
536 ctx->chacha = chacha;
537 ctx->poly = poly;
538 ctx->saltlen = ictx->saltlen;
539
Herbert Xu74790922015-07-16 12:35:08 +0800540 align = crypto_aead_alignmask(tfm);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200541 align &= ~(crypto_tfm_ctx_alignment() - 1);
Herbert Xu74790922015-07-16 12:35:08 +0800542 crypto_aead_set_reqsize(
543 tfm,
544 align + offsetof(struct chachapoly_req_ctx, u) +
545 max(offsetof(struct chacha_req, req) +
546 sizeof(struct ablkcipher_request) +
547 crypto_ablkcipher_reqsize(chacha),
548 offsetof(struct poly_req, req) +
549 sizeof(struct ahash_request) +
550 crypto_ahash_reqsize(poly)));
Martin Willi71ebc4d2015-06-01 13:44:00 +0200551
552 return 0;
553}
554
Herbert Xu74790922015-07-16 12:35:08 +0800555static void chachapoly_exit(struct crypto_aead *tfm)
Martin Willi71ebc4d2015-06-01 13:44:00 +0200556{
Herbert Xu74790922015-07-16 12:35:08 +0800557 struct chachapoly_ctx *ctx = crypto_aead_ctx(tfm);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200558
559 crypto_free_ahash(ctx->poly);
560 crypto_free_ablkcipher(ctx->chacha);
561}
562
Herbert Xu74790922015-07-16 12:35:08 +0800563static void chachapoly_free(struct aead_instance *inst)
564{
565 struct chachapoly_instance_ctx *ctx = aead_instance_ctx(inst);
566
567 crypto_drop_skcipher(&ctx->chacha);
568 crypto_drop_ahash(&ctx->poly);
569 kfree(inst);
570}
571
572static int chachapoly_create(struct crypto_template *tmpl, struct rtattr **tb,
573 const char *name, unsigned int ivsize)
Martin Willi71ebc4d2015-06-01 13:44:00 +0200574{
575 struct crypto_attr_type *algt;
Herbert Xu74790922015-07-16 12:35:08 +0800576 struct aead_instance *inst;
Martin Willi71ebc4d2015-06-01 13:44:00 +0200577 struct crypto_alg *chacha;
578 struct crypto_alg *poly;
Herbert Xu74790922015-07-16 12:35:08 +0800579 struct hash_alg_common *poly_hash;
Martin Willi71ebc4d2015-06-01 13:44:00 +0200580 struct chachapoly_instance_ctx *ctx;
581 const char *chacha_name, *poly_name;
582 int err;
583
584 if (ivsize > CHACHAPOLY_IV_SIZE)
Herbert Xu74790922015-07-16 12:35:08 +0800585 return -EINVAL;
Martin Willi71ebc4d2015-06-01 13:44:00 +0200586
587 algt = crypto_get_attr_type(tb);
588 if (IS_ERR(algt))
Herbert Xu74790922015-07-16 12:35:08 +0800589 return PTR_ERR(algt);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200590
Herbert Xu74790922015-07-16 12:35:08 +0800591 if ((algt->type ^ (CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_AEAD_NEW)) &
592 algt->mask)
593 return -EINVAL;
Martin Willi71ebc4d2015-06-01 13:44:00 +0200594
595 chacha_name = crypto_attr_alg_name(tb[1]);
596 if (IS_ERR(chacha_name))
Herbert Xu74790922015-07-16 12:35:08 +0800597 return PTR_ERR(chacha_name);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200598 poly_name = crypto_attr_alg_name(tb[2]);
599 if (IS_ERR(poly_name))
Herbert Xu74790922015-07-16 12:35:08 +0800600 return PTR_ERR(poly_name);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200601
602 poly = crypto_find_alg(poly_name, &crypto_ahash_type,
603 CRYPTO_ALG_TYPE_HASH,
604 CRYPTO_ALG_TYPE_AHASH_MASK);
605 if (IS_ERR(poly))
Herbert Xu74790922015-07-16 12:35:08 +0800606 return PTR_ERR(poly);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200607
608 err = -ENOMEM;
609 inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
610 if (!inst)
611 goto out_put_poly;
612
Herbert Xu74790922015-07-16 12:35:08 +0800613 ctx = aead_instance_ctx(inst);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200614 ctx->saltlen = CHACHAPOLY_IV_SIZE - ivsize;
Herbert Xu74790922015-07-16 12:35:08 +0800615 poly_hash = __crypto_hash_alg_common(poly);
616 err = crypto_init_ahash_spawn(&ctx->poly, poly_hash,
617 aead_crypto_instance(inst));
Martin Willi71ebc4d2015-06-01 13:44:00 +0200618 if (err)
619 goto err_free_inst;
620
Herbert Xu74790922015-07-16 12:35:08 +0800621 crypto_set_skcipher_spawn(&ctx->chacha, aead_crypto_instance(inst));
Martin Willi71ebc4d2015-06-01 13:44:00 +0200622 err = crypto_grab_skcipher(&ctx->chacha, chacha_name, 0,
623 crypto_requires_sync(algt->type,
624 algt->mask));
625 if (err)
626 goto err_drop_poly;
627
628 chacha = crypto_skcipher_spawn_alg(&ctx->chacha);
629
630 err = -EINVAL;
631 /* Need 16-byte IV size, including Initial Block Counter value */
632 if (chacha->cra_ablkcipher.ivsize != CHACHA20_IV_SIZE)
633 goto out_drop_chacha;
634 /* Not a stream cipher? */
635 if (chacha->cra_blocksize != 1)
636 goto out_drop_chacha;
637
638 err = -ENAMETOOLONG;
Herbert Xu74790922015-07-16 12:35:08 +0800639 if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME,
Martin Willi71ebc4d2015-06-01 13:44:00 +0200640 "%s(%s,%s)", name, chacha_name,
641 poly_name) >= CRYPTO_MAX_ALG_NAME)
642 goto out_drop_chacha;
Herbert Xu74790922015-07-16 12:35:08 +0800643 if (snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,
Martin Willi71ebc4d2015-06-01 13:44:00 +0200644 "%s(%s,%s)", name, chacha->cra_driver_name,
645 poly->cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
646 goto out_drop_chacha;
647
Herbert Xu74790922015-07-16 12:35:08 +0800648 inst->alg.base.cra_flags = (chacha->cra_flags | poly->cra_flags) &
649 CRYPTO_ALG_ASYNC;
650 inst->alg.base.cra_flags |= CRYPTO_ALG_AEAD_NEW;
651 inst->alg.base.cra_priority = (chacha->cra_priority +
652 poly->cra_priority) / 2;
653 inst->alg.base.cra_blocksize = 1;
654 inst->alg.base.cra_alignmask = chacha->cra_alignmask |
655 poly->cra_alignmask;
656 inst->alg.base.cra_ctxsize = sizeof(struct chachapoly_ctx) +
657 ctx->saltlen;
658 inst->alg.ivsize = ivsize;
659 inst->alg.maxauthsize = POLY1305_DIGEST_SIZE;
660 inst->alg.init = chachapoly_init;
661 inst->alg.exit = chachapoly_exit;
662 inst->alg.encrypt = chachapoly_encrypt;
663 inst->alg.decrypt = chachapoly_decrypt;
664 inst->alg.setkey = chachapoly_setkey;
665 inst->alg.setauthsize = chachapoly_setauthsize;
Martin Willi71ebc4d2015-06-01 13:44:00 +0200666
Herbert Xu74790922015-07-16 12:35:08 +0800667 inst->free = chachapoly_free;
668
669 err = aead_register_instance(tmpl, inst);
670 if (err)
671 goto out_drop_chacha;
672
673out_put_poly:
Martin Willi71ebc4d2015-06-01 13:44:00 +0200674 crypto_mod_put(poly);
Herbert Xu74790922015-07-16 12:35:08 +0800675 return err;
Martin Willi71ebc4d2015-06-01 13:44:00 +0200676
677out_drop_chacha:
678 crypto_drop_skcipher(&ctx->chacha);
679err_drop_poly:
680 crypto_drop_ahash(&ctx->poly);
681err_free_inst:
682 kfree(inst);
Herbert Xu74790922015-07-16 12:35:08 +0800683 goto out_put_poly;
Martin Willi71ebc4d2015-06-01 13:44:00 +0200684}
685
Herbert Xu74790922015-07-16 12:35:08 +0800686static int rfc7539_create(struct crypto_template *tmpl, struct rtattr **tb)
Martin Willi71ebc4d2015-06-01 13:44:00 +0200687{
Herbert Xu74790922015-07-16 12:35:08 +0800688 return chachapoly_create(tmpl, tb, "rfc7539", 12);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200689}
690
Herbert Xu74790922015-07-16 12:35:08 +0800691static int rfc7539esp_create(struct crypto_template *tmpl, struct rtattr **tb)
Martin Willi4db4ad22015-06-01 13:44:02 +0200692{
Herbert Xu74790922015-07-16 12:35:08 +0800693 return chachapoly_create(tmpl, tb, "rfc7539esp", 8);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200694}
695
696static struct crypto_template rfc7539_tmpl = {
697 .name = "rfc7539",
Herbert Xu74790922015-07-16 12:35:08 +0800698 .create = rfc7539_create,
Martin Willi71ebc4d2015-06-01 13:44:00 +0200699 .module = THIS_MODULE,
700};
701
Martin Willi4db4ad22015-06-01 13:44:02 +0200702static struct crypto_template rfc7539esp_tmpl = {
703 .name = "rfc7539esp",
Herbert Xu74790922015-07-16 12:35:08 +0800704 .create = rfc7539esp_create,
Martin Willi4db4ad22015-06-01 13:44:02 +0200705 .module = THIS_MODULE,
706};
707
Martin Willi71ebc4d2015-06-01 13:44:00 +0200708static int __init chacha20poly1305_module_init(void)
709{
Martin Willi4db4ad22015-06-01 13:44:02 +0200710 int err;
711
712 err = crypto_register_template(&rfc7539_tmpl);
713 if (err)
714 return err;
715
716 err = crypto_register_template(&rfc7539esp_tmpl);
717 if (err)
718 crypto_unregister_template(&rfc7539_tmpl);
719
720 return err;
Martin Willi71ebc4d2015-06-01 13:44:00 +0200721}
722
723static void __exit chacha20poly1305_module_exit(void)
724{
Martin Willi4db4ad22015-06-01 13:44:02 +0200725 crypto_unregister_template(&rfc7539esp_tmpl);
Martin Willi71ebc4d2015-06-01 13:44:00 +0200726 crypto_unregister_template(&rfc7539_tmpl);
727}
728
729module_init(chacha20poly1305_module_init);
730module_exit(chacha20poly1305_module_exit);
731
732MODULE_LICENSE("GPL");
733MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
734MODULE_DESCRIPTION("ChaCha20-Poly1305 AEAD");
Martin Willi71ebc4d2015-06-01 13:44:00 +0200735MODULE_ALIAS_CRYPTO("rfc7539");
Martin Willi4db4ad22015-06-01 13:44:02 +0200736MODULE_ALIAS_CRYPTO("rfc7539esp");