Blame view

crypto/chacha20poly1305.c 19.4 KB
71ebc4d1b   Martin Willi   crypto: chacha20p...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * ChaCha20-Poly1305 AEAD, RFC7539
   *
   * Copyright (C) 2015 Martin Willi
   *
   * 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/aead.h>
  #include <crypto/internal/hash.h>
  #include <crypto/internal/skcipher.h>
  #include <crypto/scatterwalk.h>
31d7247da   Martin Willi   crypto: chacha20 ...
16
  #include <crypto/chacha20.h>
2546f811e   Martin Willi   crypto: poly1305 ...
17
  #include <crypto/poly1305.h>
71ebc4d1b   Martin Willi   crypto: chacha20p...
18
19
20
21
22
23
  #include <linux/err.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
  
  #include "internal.h"
71ebc4d1b   Martin Willi   crypto: chacha20p...
24
25
26
27
28
29
30
31
32
  #define CHACHAPOLY_IV_SIZE	12
  
  struct chachapoly_instance_ctx {
  	struct crypto_skcipher_spawn chacha;
  	struct crypto_ahash_spawn poly;
  	unsigned int saltlen;
  };
  
  struct chachapoly_ctx {
1e1f00611   Herbert Xu   crypto: chacha20p...
33
  	struct crypto_skcipher *chacha;
71ebc4d1b   Martin Willi   crypto: chacha20p...
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  	struct crypto_ahash *poly;
  	/* key bytes we use for the ChaCha20 IV */
  	unsigned int saltlen;
  	u8 salt[];
  };
  
  struct poly_req {
  	/* zero byte padding for AD/ciphertext, as needed */
  	u8 pad[POLY1305_BLOCK_SIZE];
  	/* tail data with AD/ciphertext lengths */
  	struct {
  		__le64 assoclen;
  		__le64 cryptlen;
  	} tail;
  	struct scatterlist src[1];
  	struct ahash_request req; /* must be last member */
  };
  
  struct chacha_req {
71ebc4d1b   Martin Willi   crypto: chacha20p...
53
54
  	u8 iv[CHACHA20_IV_SIZE];
  	struct scatterlist src[1];
1e1f00611   Herbert Xu   crypto: chacha20p...
55
  	struct skcipher_request req; /* must be last member */
71ebc4d1b   Martin Willi   crypto: chacha20p...
56
57
58
  };
  
  struct chachapoly_req_ctx {
747909223   Herbert Xu   crypto: chacha20p...
59
60
  	struct scatterlist src[2];
  	struct scatterlist dst[2];
c2b7b20ae   Martin Willi   crypto: poly1305 ...
61
62
  	/* the key we generate for Poly1305 using Chacha20 */
  	u8 key[POLY1305_KEY_SIZE];
71ebc4d1b   Martin Willi   crypto: chacha20p...
63
64
65
66
  	/* calculated Poly1305 tag */
  	u8 tag[POLY1305_DIGEST_SIZE];
  	/* length of data to en/decrypt, without ICV */
  	unsigned int cryptlen;
747909223   Herbert Xu   crypto: chacha20p...
67
68
  	/* Actual AD, excluding IV */
  	unsigned int assoclen;
71ebc4d1b   Martin Willi   crypto: chacha20p...
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
  	union {
  		struct poly_req poly;
  		struct chacha_req chacha;
  	} u;
  };
  
  static inline void async_done_continue(struct aead_request *req, int err,
  				       int (*cont)(struct aead_request *))
  {
  	if (!err)
  		err = cont(req);
  
  	if (err != -EINPROGRESS && err != -EBUSY)
  		aead_request_complete(req, err);
  }
  
  static void chacha_iv(u8 *iv, struct aead_request *req, u32 icb)
  {
  	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
  	__le32 leicb = cpu_to_le32(icb);
  
  	memcpy(iv, &leicb, sizeof(leicb));
  	memcpy(iv + sizeof(leicb), ctx->salt, ctx->saltlen);
  	memcpy(iv + sizeof(leicb) + ctx->saltlen, req->iv,
  	       CHACHA20_IV_SIZE - sizeof(leicb) - ctx->saltlen);
  }
  
  static int poly_verify_tag(struct aead_request *req)
  {
  	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
  	u8 tag[sizeof(rctx->tag)];
747909223   Herbert Xu   crypto: chacha20p...
100
101
102
  	scatterwalk_map_and_copy(tag, req->src,
  				 req->assoclen + rctx->cryptlen,
  				 sizeof(tag), 0);
71ebc4d1b   Martin Willi   crypto: chacha20p...
103
104
105
106
107
108
109
110
  	if (crypto_memneq(tag, rctx->tag, sizeof(tag)))
  		return -EBADMSG;
  	return 0;
  }
  
  static int poly_copy_tag(struct aead_request *req)
  {
  	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
747909223   Herbert Xu   crypto: chacha20p...
111
112
  	scatterwalk_map_and_copy(rctx->tag, req->dst,
  				 req->assoclen + rctx->cryptlen,
71ebc4d1b   Martin Willi   crypto: chacha20p...
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  				 sizeof(rctx->tag), 1);
  	return 0;
  }
  
  static void chacha_decrypt_done(struct crypto_async_request *areq, int err)
  {
  	async_done_continue(areq->data, err, poly_verify_tag);
  }
  
  static int chacha_decrypt(struct aead_request *req)
  {
  	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
  	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
  	struct chacha_req *creq = &rctx->u.chacha;
747909223   Herbert Xu   crypto: chacha20p...
127
  	struct scatterlist *src, *dst;
71ebc4d1b   Martin Willi   crypto: chacha20p...
128
  	int err;
161151d79   Jason A. Donenfeld   crypto: chacha20p...
129
130
  	if (rctx->cryptlen == 0)
  		goto skip;
71ebc4d1b   Martin Willi   crypto: chacha20p...
131
  	chacha_iv(creq->iv, req, 1);
747909223   Herbert Xu   crypto: chacha20p...
132
133
134
135
136
137
138
139
  	sg_init_table(rctx->src, 2);
  	src = scatterwalk_ffwd(rctx->src, req->src, req->assoclen);
  	dst = src;
  
  	if (req->src != req->dst) {
  		sg_init_table(rctx->dst, 2);
  		dst = scatterwalk_ffwd(rctx->dst, req->dst, req->assoclen);
  	}
1e1f00611   Herbert Xu   crypto: chacha20p...
140
141
142
143
144
145
  	skcipher_request_set_callback(&creq->req, aead_request_flags(req),
  				      chacha_decrypt_done, req);
  	skcipher_request_set_tfm(&creq->req, ctx->chacha);
  	skcipher_request_set_crypt(&creq->req, src, dst,
  				   rctx->cryptlen, creq->iv);
  	err = crypto_skcipher_decrypt(&creq->req);
71ebc4d1b   Martin Willi   crypto: chacha20p...
146
147
  	if (err)
  		return err;
161151d79   Jason A. Donenfeld   crypto: chacha20p...
148
  skip:
71ebc4d1b   Martin Willi   crypto: chacha20p...
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
  	return poly_verify_tag(req);
  }
  
  static int poly_tail_continue(struct aead_request *req)
  {
  	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
  
  	if (rctx->cryptlen == req->cryptlen) /* encrypting */
  		return poly_copy_tag(req);
  
  	return chacha_decrypt(req);
  }
  
  static void poly_tail_done(struct crypto_async_request *areq, int err)
  {
  	async_done_continue(areq->data, err, poly_tail_continue);
  }
  
  static int poly_tail(struct aead_request *req)
  {
747909223   Herbert Xu   crypto: chacha20p...
169
170
  	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
  	struct chachapoly_ctx *ctx = crypto_aead_ctx(tfm);
71ebc4d1b   Martin Willi   crypto: chacha20p...
171
172
173
174
175
176
  	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
  	struct poly_req *preq = &rctx->u.poly;
  	__le64 len;
  	int err;
  
  	sg_init_table(preq->src, 1);
747909223   Herbert Xu   crypto: chacha20p...
177
  	len = cpu_to_le64(rctx->assoclen);
71ebc4d1b   Martin Willi   crypto: chacha20p...
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
  	memcpy(&preq->tail.assoclen, &len, sizeof(len));
  	len = cpu_to_le64(rctx->cryptlen);
  	memcpy(&preq->tail.cryptlen, &len, sizeof(len));
  	sg_set_buf(preq->src, &preq->tail, sizeof(preq->tail));
  
  	ahash_request_set_callback(&preq->req, aead_request_flags(req),
  				   poly_tail_done, req);
  	ahash_request_set_tfm(&preq->req, ctx->poly);
  	ahash_request_set_crypt(&preq->req, preq->src,
  				rctx->tag, sizeof(preq->tail));
  
  	err = crypto_ahash_finup(&preq->req);
  	if (err)
  		return err;
  
  	return poly_tail_continue(req);
  }
  
  static void poly_cipherpad_done(struct crypto_async_request *areq, int err)
  {
  	async_done_continue(areq->data, err, poly_tail);
  }
  
  static int poly_cipherpad(struct aead_request *req)
  {
  	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
  	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
  	struct poly_req *preq = &rctx->u.poly;
  	unsigned int padlen, bs = POLY1305_BLOCK_SIZE;
  	int err;
  
  	padlen = (bs - (rctx->cryptlen % bs)) % bs;
  	memset(preq->pad, 0, sizeof(preq->pad));
  	sg_init_table(preq->src, 1);
  	sg_set_buf(preq->src, &preq->pad, padlen);
  
  	ahash_request_set_callback(&preq->req, aead_request_flags(req),
  				   poly_cipherpad_done, req);
  	ahash_request_set_tfm(&preq->req, ctx->poly);
  	ahash_request_set_crypt(&preq->req, preq->src, NULL, padlen);
  
  	err = crypto_ahash_update(&preq->req);
  	if (err)
  		return err;
  
  	return poly_tail(req);
  }
  
  static void poly_cipher_done(struct crypto_async_request *areq, int err)
  {
  	async_done_continue(areq->data, err, poly_cipherpad);
  }
  
  static int poly_cipher(struct aead_request *req)
  {
  	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
  	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
  	struct poly_req *preq = &rctx->u.poly;
  	struct scatterlist *crypt = req->src;
  	int err;
  
  	if (rctx->cryptlen == req->cryptlen) /* encrypting */
  		crypt = req->dst;
747909223   Herbert Xu   crypto: chacha20p...
241
242
  	sg_init_table(rctx->src, 2);
  	crypt = scatterwalk_ffwd(rctx->src, crypt, req->assoclen);
71ebc4d1b   Martin Willi   crypto: chacha20p...
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
  	ahash_request_set_callback(&preq->req, aead_request_flags(req),
  				   poly_cipher_done, req);
  	ahash_request_set_tfm(&preq->req, ctx->poly);
  	ahash_request_set_crypt(&preq->req, crypt, NULL, rctx->cryptlen);
  
  	err = crypto_ahash_update(&preq->req);
  	if (err)
  		return err;
  
  	return poly_cipherpad(req);
  }
  
  static void poly_adpad_done(struct crypto_async_request *areq, int err)
  {
  	async_done_continue(areq->data, err, poly_cipher);
  }
  
  static int poly_adpad(struct aead_request *req)
  {
  	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
  	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
  	struct poly_req *preq = &rctx->u.poly;
  	unsigned int padlen, bs = POLY1305_BLOCK_SIZE;
  	int err;
747909223   Herbert Xu   crypto: chacha20p...
267
  	padlen = (bs - (rctx->assoclen % bs)) % bs;
71ebc4d1b   Martin Willi   crypto: chacha20p...
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
  	memset(preq->pad, 0, sizeof(preq->pad));
  	sg_init_table(preq->src, 1);
  	sg_set_buf(preq->src, preq->pad, padlen);
  
  	ahash_request_set_callback(&preq->req, aead_request_flags(req),
  				   poly_adpad_done, req);
  	ahash_request_set_tfm(&preq->req, ctx->poly);
  	ahash_request_set_crypt(&preq->req, preq->src, NULL, padlen);
  
  	err = crypto_ahash_update(&preq->req);
  	if (err)
  		return err;
  
  	return poly_cipher(req);
  }
  
  static void poly_ad_done(struct crypto_async_request *areq, int err)
  {
  	async_done_continue(areq->data, err, poly_adpad);
  }
  
  static int poly_ad(struct aead_request *req)
  {
  	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
  	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
  	struct poly_req *preq = &rctx->u.poly;
  	int err;
  
  	ahash_request_set_callback(&preq->req, aead_request_flags(req),
  				   poly_ad_done, req);
  	ahash_request_set_tfm(&preq->req, ctx->poly);
747909223   Herbert Xu   crypto: chacha20p...
299
  	ahash_request_set_crypt(&preq->req, req->src, NULL, rctx->assoclen);
71ebc4d1b   Martin Willi   crypto: chacha20p...
300
301
302
303
304
305
306
  
  	err = crypto_ahash_update(&preq->req);
  	if (err)
  		return err;
  
  	return poly_adpad(req);
  }
c2b7b20ae   Martin Willi   crypto: poly1305 ...
307
  static void poly_setkey_done(struct crypto_async_request *areq, int err)
71ebc4d1b   Martin Willi   crypto: chacha20p...
308
309
310
  {
  	async_done_continue(areq->data, err, poly_ad);
  }
c2b7b20ae   Martin Willi   crypto: poly1305 ...
311
  static int poly_setkey(struct aead_request *req)
71ebc4d1b   Martin Willi   crypto: chacha20p...
312
313
314
315
316
  {
  	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
  	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
  	struct poly_req *preq = &rctx->u.poly;
  	int err;
c2b7b20ae   Martin Willi   crypto: poly1305 ...
317
318
  	sg_init_table(preq->src, 1);
  	sg_set_buf(preq->src, rctx->key, sizeof(rctx->key));
71ebc4d1b   Martin Willi   crypto: chacha20p...
319
  	ahash_request_set_callback(&preq->req, aead_request_flags(req),
c2b7b20ae   Martin Willi   crypto: poly1305 ...
320
  				   poly_setkey_done, req);
71ebc4d1b   Martin Willi   crypto: chacha20p...
321
  	ahash_request_set_tfm(&preq->req, ctx->poly);
c2b7b20ae   Martin Willi   crypto: poly1305 ...
322
  	ahash_request_set_crypt(&preq->req, preq->src, NULL, sizeof(rctx->key));
71ebc4d1b   Martin Willi   crypto: chacha20p...
323

c2b7b20ae   Martin Willi   crypto: poly1305 ...
324
  	err = crypto_ahash_update(&preq->req);
71ebc4d1b   Martin Willi   crypto: chacha20p...
325
326
327
328
329
  	if (err)
  		return err;
  
  	return poly_ad(req);
  }
c2b7b20ae   Martin Willi   crypto: poly1305 ...
330
  static void poly_init_done(struct crypto_async_request *areq, int err)
71ebc4d1b   Martin Willi   crypto: chacha20p...
331
  {
c2b7b20ae   Martin Willi   crypto: poly1305 ...
332
333
334
335
336
337
  	async_done_continue(areq->data, err, poly_setkey);
  }
  
  static int poly_init(struct aead_request *req)
  {
  	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
71ebc4d1b   Martin Willi   crypto: chacha20p...
338
  	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
c2b7b20ae   Martin Willi   crypto: poly1305 ...
339
  	struct poly_req *preq = &rctx->u.poly;
71ebc4d1b   Martin Willi   crypto: chacha20p...
340
  	int err;
c2b7b20ae   Martin Willi   crypto: poly1305 ...
341
342
343
  	ahash_request_set_callback(&preq->req, aead_request_flags(req),
  				   poly_init_done, req);
  	ahash_request_set_tfm(&preq->req, ctx->poly);
71ebc4d1b   Martin Willi   crypto: chacha20p...
344

c2b7b20ae   Martin Willi   crypto: poly1305 ...
345
  	err = crypto_ahash_init(&preq->req);
71ebc4d1b   Martin Willi   crypto: chacha20p...
346
347
  	if (err)
  		return err;
c2b7b20ae   Martin Willi   crypto: poly1305 ...
348
  	return poly_setkey(req);
71ebc4d1b   Martin Willi   crypto: chacha20p...
349
350
351
352
  }
  
  static void poly_genkey_done(struct crypto_async_request *areq, int err)
  {
c2b7b20ae   Martin Willi   crypto: poly1305 ...
353
  	async_done_continue(areq->data, err, poly_init);
71ebc4d1b   Martin Willi   crypto: chacha20p...
354
355
356
357
  }
  
  static int poly_genkey(struct aead_request *req)
  {
747909223   Herbert Xu   crypto: chacha20p...
358
359
  	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
  	struct chachapoly_ctx *ctx = crypto_aead_ctx(tfm);
71ebc4d1b   Martin Willi   crypto: chacha20p...
360
361
362
  	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
  	struct chacha_req *creq = &rctx->u.chacha;
  	int err;
747909223   Herbert Xu   crypto: chacha20p...
363
364
365
366
367
368
369
  	rctx->assoclen = req->assoclen;
  
  	if (crypto_aead_ivsize(tfm) == 8) {
  		if (rctx->assoclen < 8)
  			return -EINVAL;
  		rctx->assoclen -= 8;
  	}
71ebc4d1b   Martin Willi   crypto: chacha20p...
370
  	sg_init_table(creq->src, 1);
c2b7b20ae   Martin Willi   crypto: poly1305 ...
371
372
  	memset(rctx->key, 0, sizeof(rctx->key));
  	sg_set_buf(creq->src, rctx->key, sizeof(rctx->key));
71ebc4d1b   Martin Willi   crypto: chacha20p...
373
374
  
  	chacha_iv(creq->iv, req, 0);
1e1f00611   Herbert Xu   crypto: chacha20p...
375
376
377
378
379
  	skcipher_request_set_callback(&creq->req, aead_request_flags(req),
  				      poly_genkey_done, req);
  	skcipher_request_set_tfm(&creq->req, ctx->chacha);
  	skcipher_request_set_crypt(&creq->req, creq->src, creq->src,
  				   POLY1305_KEY_SIZE, creq->iv);
71ebc4d1b   Martin Willi   crypto: chacha20p...
380

1e1f00611   Herbert Xu   crypto: chacha20p...
381
  	err = crypto_skcipher_decrypt(&creq->req);
71ebc4d1b   Martin Willi   crypto: chacha20p...
382
383
  	if (err)
  		return err;
c2b7b20ae   Martin Willi   crypto: poly1305 ...
384
  	return poly_init(req);
71ebc4d1b   Martin Willi   crypto: chacha20p...
385
386
387
388
389
390
391
392
393
394
395
396
  }
  
  static void chacha_encrypt_done(struct crypto_async_request *areq, int err)
  {
  	async_done_continue(areq->data, err, poly_genkey);
  }
  
  static int chacha_encrypt(struct aead_request *req)
  {
  	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
  	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
  	struct chacha_req *creq = &rctx->u.chacha;
747909223   Herbert Xu   crypto: chacha20p...
397
  	struct scatterlist *src, *dst;
71ebc4d1b   Martin Willi   crypto: chacha20p...
398
  	int err;
161151d79   Jason A. Donenfeld   crypto: chacha20p...
399
400
  	if (req->cryptlen == 0)
  		goto skip;
71ebc4d1b   Martin Willi   crypto: chacha20p...
401
  	chacha_iv(creq->iv, req, 1);
747909223   Herbert Xu   crypto: chacha20p...
402
403
404
405
406
407
408
409
  	sg_init_table(rctx->src, 2);
  	src = scatterwalk_ffwd(rctx->src, req->src, req->assoclen);
  	dst = src;
  
  	if (req->src != req->dst) {
  		sg_init_table(rctx->dst, 2);
  		dst = scatterwalk_ffwd(rctx->dst, req->dst, req->assoclen);
  	}
1e1f00611   Herbert Xu   crypto: chacha20p...
410
411
412
413
414
415
  	skcipher_request_set_callback(&creq->req, aead_request_flags(req),
  				      chacha_encrypt_done, req);
  	skcipher_request_set_tfm(&creq->req, ctx->chacha);
  	skcipher_request_set_crypt(&creq->req, src, dst,
  				   req->cryptlen, creq->iv);
  	err = crypto_skcipher_encrypt(&creq->req);
71ebc4d1b   Martin Willi   crypto: chacha20p...
416
417
  	if (err)
  		return err;
161151d79   Jason A. Donenfeld   crypto: chacha20p...
418
  skip:
71ebc4d1b   Martin Willi   crypto: chacha20p...
419
420
421
422
423
424
425
426
427
428
429
  	return poly_genkey(req);
  }
  
  static int chachapoly_encrypt(struct aead_request *req)
  {
  	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
  
  	rctx->cryptlen = req->cryptlen;
  
  	/* encrypt call chain:
  	 * - chacha_encrypt/done()
c2b7b20ae   Martin Willi   crypto: poly1305 ...
430
  	 * - poly_genkey/done()
71ebc4d1b   Martin Willi   crypto: chacha20p...
431
  	 * - poly_init/done()
c2b7b20ae   Martin Willi   crypto: poly1305 ...
432
  	 * - poly_setkey/done()
71ebc4d1b   Martin Willi   crypto: chacha20p...
433
434
435
436
437
438
439
440
441
442
443
444
445
  	 * - poly_ad/done()
  	 * - poly_adpad/done()
  	 * - poly_cipher/done()
  	 * - poly_cipherpad/done()
  	 * - poly_tail/done/continue()
  	 * - poly_copy_tag()
  	 */
  	return chacha_encrypt(req);
  }
  
  static int chachapoly_decrypt(struct aead_request *req)
  {
  	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
71ebc4d1b   Martin Willi   crypto: chacha20p...
446
447
448
  	rctx->cryptlen = req->cryptlen - POLY1305_DIGEST_SIZE;
  
  	/* decrypt call chain:
c2b7b20ae   Martin Willi   crypto: poly1305 ...
449
  	 * - poly_genkey/done()
71ebc4d1b   Martin Willi   crypto: chacha20p...
450
  	 * - poly_init/done()
c2b7b20ae   Martin Willi   crypto: poly1305 ...
451
  	 * - poly_setkey/done()
71ebc4d1b   Martin Willi   crypto: chacha20p...
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
  	 * - poly_ad/done()
  	 * - poly_adpad/done()
  	 * - poly_cipher/done()
  	 * - poly_cipherpad/done()
  	 * - poly_tail/done/continue()
  	 * - chacha_decrypt/done()
  	 * - poly_verify_tag()
  	 */
  	return poly_genkey(req);
  }
  
  static int chachapoly_setkey(struct crypto_aead *aead, const u8 *key,
  			     unsigned int keylen)
  {
  	struct chachapoly_ctx *ctx = crypto_aead_ctx(aead);
  	int err;
  
  	if (keylen != ctx->saltlen + CHACHA20_KEY_SIZE)
  		return -EINVAL;
  
  	keylen -= ctx->saltlen;
  	memcpy(ctx->salt, key + keylen, ctx->saltlen);
1e1f00611   Herbert Xu   crypto: chacha20p...
474
475
476
  	crypto_skcipher_clear_flags(ctx->chacha, CRYPTO_TFM_REQ_MASK);
  	crypto_skcipher_set_flags(ctx->chacha, crypto_aead_get_flags(aead) &
  					       CRYPTO_TFM_REQ_MASK);
71ebc4d1b   Martin Willi   crypto: chacha20p...
477

1e1f00611   Herbert Xu   crypto: chacha20p...
478
479
480
  	err = crypto_skcipher_setkey(ctx->chacha, key, keylen);
  	crypto_aead_set_flags(aead, crypto_skcipher_get_flags(ctx->chacha) &
  				    CRYPTO_TFM_RES_MASK);
71ebc4d1b   Martin Willi   crypto: chacha20p...
481
482
483
484
485
486
487
488
489
490
491
  	return err;
  }
  
  static int chachapoly_setauthsize(struct crypto_aead *tfm,
  				  unsigned int authsize)
  {
  	if (authsize != POLY1305_DIGEST_SIZE)
  		return -EINVAL;
  
  	return 0;
  }
747909223   Herbert Xu   crypto: chacha20p...
492
  static int chachapoly_init(struct crypto_aead *tfm)
71ebc4d1b   Martin Willi   crypto: chacha20p...
493
  {
747909223   Herbert Xu   crypto: chacha20p...
494
495
496
  	struct aead_instance *inst = aead_alg_instance(tfm);
  	struct chachapoly_instance_ctx *ictx = aead_instance_ctx(inst);
  	struct chachapoly_ctx *ctx = crypto_aead_ctx(tfm);
1e1f00611   Herbert Xu   crypto: chacha20p...
497
  	struct crypto_skcipher *chacha;
71ebc4d1b   Martin Willi   crypto: chacha20p...
498
499
500
501
502
503
  	struct crypto_ahash *poly;
  	unsigned long align;
  
  	poly = crypto_spawn_ahash(&ictx->poly);
  	if (IS_ERR(poly))
  		return PTR_ERR(poly);
60425a8ba   Eric Biggers   crypto: skcipher ...
504
  	chacha = crypto_spawn_skcipher(&ictx->chacha);
71ebc4d1b   Martin Willi   crypto: chacha20p...
505
506
507
508
509
510
511
512
  	if (IS_ERR(chacha)) {
  		crypto_free_ahash(poly);
  		return PTR_ERR(chacha);
  	}
  
  	ctx->chacha = chacha;
  	ctx->poly = poly;
  	ctx->saltlen = ictx->saltlen;
747909223   Herbert Xu   crypto: chacha20p...
513
  	align = crypto_aead_alignmask(tfm);
71ebc4d1b   Martin Willi   crypto: chacha20p...
514
  	align &= ~(crypto_tfm_ctx_alignment() - 1);
747909223   Herbert Xu   crypto: chacha20p...
515
516
517
518
  	crypto_aead_set_reqsize(
  		tfm,
  		align + offsetof(struct chachapoly_req_ctx, u) +
  		max(offsetof(struct chacha_req, req) +
1e1f00611   Herbert Xu   crypto: chacha20p...
519
520
  		    sizeof(struct skcipher_request) +
  		    crypto_skcipher_reqsize(chacha),
747909223   Herbert Xu   crypto: chacha20p...
521
522
523
  		    offsetof(struct poly_req, req) +
  		    sizeof(struct ahash_request) +
  		    crypto_ahash_reqsize(poly)));
71ebc4d1b   Martin Willi   crypto: chacha20p...
524
525
526
  
  	return 0;
  }
747909223   Herbert Xu   crypto: chacha20p...
527
  static void chachapoly_exit(struct crypto_aead *tfm)
71ebc4d1b   Martin Willi   crypto: chacha20p...
528
  {
747909223   Herbert Xu   crypto: chacha20p...
529
  	struct chachapoly_ctx *ctx = crypto_aead_ctx(tfm);
71ebc4d1b   Martin Willi   crypto: chacha20p...
530
531
  
  	crypto_free_ahash(ctx->poly);
1e1f00611   Herbert Xu   crypto: chacha20p...
532
  	crypto_free_skcipher(ctx->chacha);
71ebc4d1b   Martin Willi   crypto: chacha20p...
533
  }
747909223   Herbert Xu   crypto: chacha20p...
534
535
536
537
538
539
540
541
542
543
544
  static void chachapoly_free(struct aead_instance *inst)
  {
  	struct chachapoly_instance_ctx *ctx = aead_instance_ctx(inst);
  
  	crypto_drop_skcipher(&ctx->chacha);
  	crypto_drop_ahash(&ctx->poly);
  	kfree(inst);
  }
  
  static int chachapoly_create(struct crypto_template *tmpl, struct rtattr **tb,
  			     const char *name, unsigned int ivsize)
71ebc4d1b   Martin Willi   crypto: chacha20p...
545
546
  {
  	struct crypto_attr_type *algt;
747909223   Herbert Xu   crypto: chacha20p...
547
  	struct aead_instance *inst;
1e1f00611   Herbert Xu   crypto: chacha20p...
548
  	struct skcipher_alg *chacha;
71ebc4d1b   Martin Willi   crypto: chacha20p...
549
  	struct crypto_alg *poly;
747909223   Herbert Xu   crypto: chacha20p...
550
  	struct hash_alg_common *poly_hash;
71ebc4d1b   Martin Willi   crypto: chacha20p...
551
552
553
554
555
  	struct chachapoly_instance_ctx *ctx;
  	const char *chacha_name, *poly_name;
  	int err;
  
  	if (ivsize > CHACHAPOLY_IV_SIZE)
747909223   Herbert Xu   crypto: chacha20p...
556
  		return -EINVAL;
71ebc4d1b   Martin Willi   crypto: chacha20p...
557
558
559
  
  	algt = crypto_get_attr_type(tb);
  	if (IS_ERR(algt))
747909223   Herbert Xu   crypto: chacha20p...
560
  		return PTR_ERR(algt);
71ebc4d1b   Martin Willi   crypto: chacha20p...
561

5e4b8c1fc   Herbert Xu   crypto: aead - Re...
562
  	if ((algt->type ^ CRYPTO_ALG_TYPE_AEAD) & algt->mask)
747909223   Herbert Xu   crypto: chacha20p...
563
  		return -EINVAL;
71ebc4d1b   Martin Willi   crypto: chacha20p...
564
565
566
  
  	chacha_name = crypto_attr_alg_name(tb[1]);
  	if (IS_ERR(chacha_name))
747909223   Herbert Xu   crypto: chacha20p...
567
  		return PTR_ERR(chacha_name);
71ebc4d1b   Martin Willi   crypto: chacha20p...
568
569
  	poly_name = crypto_attr_alg_name(tb[2]);
  	if (IS_ERR(poly_name))
747909223   Herbert Xu   crypto: chacha20p...
570
  		return PTR_ERR(poly_name);
71ebc4d1b   Martin Willi   crypto: chacha20p...
571
572
573
  
  	poly = crypto_find_alg(poly_name, &crypto_ahash_type,
  			       CRYPTO_ALG_TYPE_HASH,
1e1f00611   Herbert Xu   crypto: chacha20p...
574
575
576
  			       CRYPTO_ALG_TYPE_AHASH_MASK |
  			       crypto_requires_sync(algt->type,
  						    algt->mask));
71ebc4d1b   Martin Willi   crypto: chacha20p...
577
  	if (IS_ERR(poly))
747909223   Herbert Xu   crypto: chacha20p...
578
  		return PTR_ERR(poly);
9c36498f7   Eric Biggers   crypto: chacha20p...
579
580
581
582
583
  	poly_hash = __crypto_hash_alg_common(poly);
  
  	err = -EINVAL;
  	if (poly_hash->digestsize != POLY1305_DIGEST_SIZE)
  		goto out_put_poly;
71ebc4d1b   Martin Willi   crypto: chacha20p...
584
585
586
587
588
  
  	err = -ENOMEM;
  	inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
  	if (!inst)
  		goto out_put_poly;
747909223   Herbert Xu   crypto: chacha20p...
589
  	ctx = aead_instance_ctx(inst);
71ebc4d1b   Martin Willi   crypto: chacha20p...
590
  	ctx->saltlen = CHACHAPOLY_IV_SIZE - ivsize;
747909223   Herbert Xu   crypto: chacha20p...
591
592
  	err = crypto_init_ahash_spawn(&ctx->poly, poly_hash,
  				      aead_crypto_instance(inst));
71ebc4d1b   Martin Willi   crypto: chacha20p...
593
594
  	if (err)
  		goto err_free_inst;
747909223   Herbert Xu   crypto: chacha20p...
595
  	crypto_set_skcipher_spawn(&ctx->chacha, aead_crypto_instance(inst));
a35528eca   Eric Biggers   crypto: skcipher ...
596
597
598
  	err = crypto_grab_skcipher(&ctx->chacha, chacha_name, 0,
  				   crypto_requires_sync(algt->type,
  							algt->mask));
71ebc4d1b   Martin Willi   crypto: chacha20p...
599
600
  	if (err)
  		goto err_drop_poly;
1e1f00611   Herbert Xu   crypto: chacha20p...
601
  	chacha = crypto_spawn_skcipher_alg(&ctx->chacha);
71ebc4d1b   Martin Willi   crypto: chacha20p...
602
603
604
  
  	err = -EINVAL;
  	/* Need 16-byte IV size, including Initial Block Counter value */
1e1f00611   Herbert Xu   crypto: chacha20p...
605
  	if (crypto_skcipher_alg_ivsize(chacha) != CHACHA20_IV_SIZE)
71ebc4d1b   Martin Willi   crypto: chacha20p...
606
607
  		goto out_drop_chacha;
  	/* Not a stream cipher? */
1e1f00611   Herbert Xu   crypto: chacha20p...
608
  	if (chacha->base.cra_blocksize != 1)
71ebc4d1b   Martin Willi   crypto: chacha20p...
609
610
611
  		goto out_drop_chacha;
  
  	err = -ENAMETOOLONG;
747909223   Herbert Xu   crypto: chacha20p...
612
  	if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME,
71ebc4d1b   Martin Willi   crypto: chacha20p...
613
614
615
  		     "%s(%s,%s)", name, chacha_name,
  		     poly_name) >= CRYPTO_MAX_ALG_NAME)
  		goto out_drop_chacha;
747909223   Herbert Xu   crypto: chacha20p...
616
  	if (snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,
1e1f00611   Herbert Xu   crypto: chacha20p...
617
  		     "%s(%s,%s)", name, chacha->base.cra_driver_name,
71ebc4d1b   Martin Willi   crypto: chacha20p...
618
619
  		     poly->cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
  		goto out_drop_chacha;
1e1f00611   Herbert Xu   crypto: chacha20p...
620
  	inst->alg.base.cra_flags = (chacha->base.cra_flags | poly->cra_flags) &
747909223   Herbert Xu   crypto: chacha20p...
621
  				   CRYPTO_ALG_ASYNC;
1e1f00611   Herbert Xu   crypto: chacha20p...
622
  	inst->alg.base.cra_priority = (chacha->base.cra_priority +
747909223   Herbert Xu   crypto: chacha20p...
623
624
  				       poly->cra_priority) / 2;
  	inst->alg.base.cra_blocksize = 1;
1e1f00611   Herbert Xu   crypto: chacha20p...
625
  	inst->alg.base.cra_alignmask = chacha->base.cra_alignmask |
747909223   Herbert Xu   crypto: chacha20p...
626
627
628
629
  				       poly->cra_alignmask;
  	inst->alg.base.cra_ctxsize = sizeof(struct chachapoly_ctx) +
  				     ctx->saltlen;
  	inst->alg.ivsize = ivsize;
1e1f00611   Herbert Xu   crypto: chacha20p...
630
  	inst->alg.chunksize = crypto_skcipher_alg_chunksize(chacha);
747909223   Herbert Xu   crypto: chacha20p...
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
  	inst->alg.maxauthsize = POLY1305_DIGEST_SIZE;
  	inst->alg.init = chachapoly_init;
  	inst->alg.exit = chachapoly_exit;
  	inst->alg.encrypt = chachapoly_encrypt;
  	inst->alg.decrypt = chachapoly_decrypt;
  	inst->alg.setkey = chachapoly_setkey;
  	inst->alg.setauthsize = chachapoly_setauthsize;
  
  	inst->free = chachapoly_free;
  
  	err = aead_register_instance(tmpl, inst);
  	if (err)
  		goto out_drop_chacha;
  
  out_put_poly:
71ebc4d1b   Martin Willi   crypto: chacha20p...
646
  	crypto_mod_put(poly);
747909223   Herbert Xu   crypto: chacha20p...
647
  	return err;
71ebc4d1b   Martin Willi   crypto: chacha20p...
648
649
650
651
652
653
654
  
  out_drop_chacha:
  	crypto_drop_skcipher(&ctx->chacha);
  err_drop_poly:
  	crypto_drop_ahash(&ctx->poly);
  err_free_inst:
  	kfree(inst);
747909223   Herbert Xu   crypto: chacha20p...
655
  	goto out_put_poly;
71ebc4d1b   Martin Willi   crypto: chacha20p...
656
  }
747909223   Herbert Xu   crypto: chacha20p...
657
  static int rfc7539_create(struct crypto_template *tmpl, struct rtattr **tb)
4db4ad260   Martin Willi   crypto: chacha20p...
658
  {
747909223   Herbert Xu   crypto: chacha20p...
659
  	return chachapoly_create(tmpl, tb, "rfc7539", 12);
4db4ad260   Martin Willi   crypto: chacha20p...
660
  }
747909223   Herbert Xu   crypto: chacha20p...
661
  static int rfc7539esp_create(struct crypto_template *tmpl, struct rtattr **tb)
71ebc4d1b   Martin Willi   crypto: chacha20p...
662
  {
747909223   Herbert Xu   crypto: chacha20p...
663
  	return chachapoly_create(tmpl, tb, "rfc7539esp", 8);
71ebc4d1b   Martin Willi   crypto: chacha20p...
664
665
666
667
  }
  
  static struct crypto_template rfc7539_tmpl = {
  	.name = "rfc7539",
747909223   Herbert Xu   crypto: chacha20p...
668
  	.create = rfc7539_create,
71ebc4d1b   Martin Willi   crypto: chacha20p...
669
670
  	.module = THIS_MODULE,
  };
4db4ad260   Martin Willi   crypto: chacha20p...
671
672
  static struct crypto_template rfc7539esp_tmpl = {
  	.name = "rfc7539esp",
747909223   Herbert Xu   crypto: chacha20p...
673
  	.create = rfc7539esp_create,
4db4ad260   Martin Willi   crypto: chacha20p...
674
675
  	.module = THIS_MODULE,
  };
71ebc4d1b   Martin Willi   crypto: chacha20p...
676
677
  static int __init chacha20poly1305_module_init(void)
  {
4db4ad260   Martin Willi   crypto: chacha20p...
678
679
680
681
682
683
684
685
686
687
688
  	int err;
  
  	err = crypto_register_template(&rfc7539_tmpl);
  	if (err)
  		return err;
  
  	err = crypto_register_template(&rfc7539esp_tmpl);
  	if (err)
  		crypto_unregister_template(&rfc7539_tmpl);
  
  	return err;
71ebc4d1b   Martin Willi   crypto: chacha20p...
689
690
691
692
  }
  
  static void __exit chacha20poly1305_module_exit(void)
  {
4db4ad260   Martin Willi   crypto: chacha20p...
693
  	crypto_unregister_template(&rfc7539esp_tmpl);
71ebc4d1b   Martin Willi   crypto: chacha20p...
694
695
696
697
698
699
700
701
702
  	crypto_unregister_template(&rfc7539_tmpl);
  }
  
  module_init(chacha20poly1305_module_init);
  module_exit(chacha20poly1305_module_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
  MODULE_DESCRIPTION("ChaCha20-Poly1305 AEAD");
71ebc4d1b   Martin Willi   crypto: chacha20p...
703
  MODULE_ALIAS_CRYPTO("rfc7539");
4db4ad260   Martin Willi   crypto: chacha20p...
704
  MODULE_ALIAS_CRYPTO("rfc7539esp");