162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ChaCha20-Poly1305 AEAD, RFC7539
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 Martin Willi
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <crypto/internal/aead.h>
962306a36Sopenharmony_ci#include <crypto/internal/hash.h>
1062306a36Sopenharmony_ci#include <crypto/internal/skcipher.h>
1162306a36Sopenharmony_ci#include <crypto/scatterwalk.h>
1262306a36Sopenharmony_ci#include <crypto/chacha.h>
1362306a36Sopenharmony_ci#include <crypto/poly1305.h>
1462306a36Sopenharmony_ci#include <linux/err.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistruct chachapoly_instance_ctx {
2062306a36Sopenharmony_ci	struct crypto_skcipher_spawn chacha;
2162306a36Sopenharmony_ci	struct crypto_ahash_spawn poly;
2262306a36Sopenharmony_ci	unsigned int saltlen;
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct chachapoly_ctx {
2662306a36Sopenharmony_ci	struct crypto_skcipher *chacha;
2762306a36Sopenharmony_ci	struct crypto_ahash *poly;
2862306a36Sopenharmony_ci	/* key bytes we use for the ChaCha20 IV */
2962306a36Sopenharmony_ci	unsigned int saltlen;
3062306a36Sopenharmony_ci	u8 salt[];
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct poly_req {
3462306a36Sopenharmony_ci	/* zero byte padding for AD/ciphertext, as needed */
3562306a36Sopenharmony_ci	u8 pad[POLY1305_BLOCK_SIZE];
3662306a36Sopenharmony_ci	/* tail data with AD/ciphertext lengths */
3762306a36Sopenharmony_ci	struct {
3862306a36Sopenharmony_ci		__le64 assoclen;
3962306a36Sopenharmony_ci		__le64 cryptlen;
4062306a36Sopenharmony_ci	} tail;
4162306a36Sopenharmony_ci	struct scatterlist src[1];
4262306a36Sopenharmony_ci	struct ahash_request req; /* must be last member */
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistruct chacha_req {
4662306a36Sopenharmony_ci	u8 iv[CHACHA_IV_SIZE];
4762306a36Sopenharmony_ci	struct scatterlist src[1];
4862306a36Sopenharmony_ci	struct skcipher_request req; /* must be last member */
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistruct chachapoly_req_ctx {
5262306a36Sopenharmony_ci	struct scatterlist src[2];
5362306a36Sopenharmony_ci	struct scatterlist dst[2];
5462306a36Sopenharmony_ci	/* the key we generate for Poly1305 using Chacha20 */
5562306a36Sopenharmony_ci	u8 key[POLY1305_KEY_SIZE];
5662306a36Sopenharmony_ci	/* calculated Poly1305 tag */
5762306a36Sopenharmony_ci	u8 tag[POLY1305_DIGEST_SIZE];
5862306a36Sopenharmony_ci	/* length of data to en/decrypt, without ICV */
5962306a36Sopenharmony_ci	unsigned int cryptlen;
6062306a36Sopenharmony_ci	/* Actual AD, excluding IV */
6162306a36Sopenharmony_ci	unsigned int assoclen;
6262306a36Sopenharmony_ci	/* request flags, with MAY_SLEEP cleared if needed */
6362306a36Sopenharmony_ci	u32 flags;
6462306a36Sopenharmony_ci	union {
6562306a36Sopenharmony_ci		struct poly_req poly;
6662306a36Sopenharmony_ci		struct chacha_req chacha;
6762306a36Sopenharmony_ci	} u;
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic inline void async_done_continue(struct aead_request *req, int err,
7162306a36Sopenharmony_ci				       int (*cont)(struct aead_request *))
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	if (!err) {
7462306a36Sopenharmony_ci		struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci		rctx->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
7762306a36Sopenharmony_ci		err = cont(req);
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (err != -EINPROGRESS && err != -EBUSY)
8162306a36Sopenharmony_ci		aead_request_complete(req, err);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic void chacha_iv(u8 *iv, struct aead_request *req, u32 icb)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
8762306a36Sopenharmony_ci	__le32 leicb = cpu_to_le32(icb);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	memcpy(iv, &leicb, sizeof(leicb));
9062306a36Sopenharmony_ci	memcpy(iv + sizeof(leicb), ctx->salt, ctx->saltlen);
9162306a36Sopenharmony_ci	memcpy(iv + sizeof(leicb) + ctx->saltlen, req->iv,
9262306a36Sopenharmony_ci	       CHACHA_IV_SIZE - sizeof(leicb) - ctx->saltlen);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic int poly_verify_tag(struct aead_request *req)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
9862306a36Sopenharmony_ci	u8 tag[sizeof(rctx->tag)];
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	scatterwalk_map_and_copy(tag, req->src,
10162306a36Sopenharmony_ci				 req->assoclen + rctx->cryptlen,
10262306a36Sopenharmony_ci				 sizeof(tag), 0);
10362306a36Sopenharmony_ci	if (crypto_memneq(tag, rctx->tag, sizeof(tag)))
10462306a36Sopenharmony_ci		return -EBADMSG;
10562306a36Sopenharmony_ci	return 0;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int poly_copy_tag(struct aead_request *req)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	scatterwalk_map_and_copy(rctx->tag, req->dst,
11362306a36Sopenharmony_ci				 req->assoclen + rctx->cryptlen,
11462306a36Sopenharmony_ci				 sizeof(rctx->tag), 1);
11562306a36Sopenharmony_ci	return 0;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void chacha_decrypt_done(void *data, int err)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	async_done_continue(data, err, poly_verify_tag);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic int chacha_decrypt(struct aead_request *req)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
12662306a36Sopenharmony_ci	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
12762306a36Sopenharmony_ci	struct chacha_req *creq = &rctx->u.chacha;
12862306a36Sopenharmony_ci	struct scatterlist *src, *dst;
12962306a36Sopenharmony_ci	int err;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (rctx->cryptlen == 0)
13262306a36Sopenharmony_ci		goto skip;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	chacha_iv(creq->iv, req, 1);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	src = scatterwalk_ffwd(rctx->src, req->src, req->assoclen);
13762306a36Sopenharmony_ci	dst = src;
13862306a36Sopenharmony_ci	if (req->src != req->dst)
13962306a36Sopenharmony_ci		dst = scatterwalk_ffwd(rctx->dst, req->dst, req->assoclen);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	skcipher_request_set_callback(&creq->req, rctx->flags,
14262306a36Sopenharmony_ci				      chacha_decrypt_done, req);
14362306a36Sopenharmony_ci	skcipher_request_set_tfm(&creq->req, ctx->chacha);
14462306a36Sopenharmony_ci	skcipher_request_set_crypt(&creq->req, src, dst,
14562306a36Sopenharmony_ci				   rctx->cryptlen, creq->iv);
14662306a36Sopenharmony_ci	err = crypto_skcipher_decrypt(&creq->req);
14762306a36Sopenharmony_ci	if (err)
14862306a36Sopenharmony_ci		return err;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ciskip:
15162306a36Sopenharmony_ci	return poly_verify_tag(req);
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic int poly_tail_continue(struct aead_request *req)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (rctx->cryptlen == req->cryptlen) /* encrypting */
15962306a36Sopenharmony_ci		return poly_copy_tag(req);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return chacha_decrypt(req);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic void poly_tail_done(void *data, int err)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	async_done_continue(data, err, poly_tail_continue);
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int poly_tail(struct aead_request *req)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
17262306a36Sopenharmony_ci	struct chachapoly_ctx *ctx = crypto_aead_ctx(tfm);
17362306a36Sopenharmony_ci	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
17462306a36Sopenharmony_ci	struct poly_req *preq = &rctx->u.poly;
17562306a36Sopenharmony_ci	int err;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	preq->tail.assoclen = cpu_to_le64(rctx->assoclen);
17862306a36Sopenharmony_ci	preq->tail.cryptlen = cpu_to_le64(rctx->cryptlen);
17962306a36Sopenharmony_ci	sg_init_one(preq->src, &preq->tail, sizeof(preq->tail));
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	ahash_request_set_callback(&preq->req, rctx->flags,
18262306a36Sopenharmony_ci				   poly_tail_done, req);
18362306a36Sopenharmony_ci	ahash_request_set_tfm(&preq->req, ctx->poly);
18462306a36Sopenharmony_ci	ahash_request_set_crypt(&preq->req, preq->src,
18562306a36Sopenharmony_ci				rctx->tag, sizeof(preq->tail));
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	err = crypto_ahash_finup(&preq->req);
18862306a36Sopenharmony_ci	if (err)
18962306a36Sopenharmony_ci		return err;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return poly_tail_continue(req);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic void poly_cipherpad_done(void *data, int err)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	async_done_continue(data, err, poly_tail);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int poly_cipherpad(struct aead_request *req)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
20262306a36Sopenharmony_ci	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
20362306a36Sopenharmony_ci	struct poly_req *preq = &rctx->u.poly;
20462306a36Sopenharmony_ci	unsigned int padlen;
20562306a36Sopenharmony_ci	int err;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	padlen = -rctx->cryptlen % POLY1305_BLOCK_SIZE;
20862306a36Sopenharmony_ci	memset(preq->pad, 0, sizeof(preq->pad));
20962306a36Sopenharmony_ci	sg_init_one(preq->src, preq->pad, padlen);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ahash_request_set_callback(&preq->req, rctx->flags,
21262306a36Sopenharmony_ci				   poly_cipherpad_done, req);
21362306a36Sopenharmony_ci	ahash_request_set_tfm(&preq->req, ctx->poly);
21462306a36Sopenharmony_ci	ahash_request_set_crypt(&preq->req, preq->src, NULL, padlen);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	err = crypto_ahash_update(&preq->req);
21762306a36Sopenharmony_ci	if (err)
21862306a36Sopenharmony_ci		return err;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	return poly_tail(req);
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic void poly_cipher_done(void *data, int err)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	async_done_continue(data, err, poly_cipherpad);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int poly_cipher(struct aead_request *req)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
23162306a36Sopenharmony_ci	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
23262306a36Sopenharmony_ci	struct poly_req *preq = &rctx->u.poly;
23362306a36Sopenharmony_ci	struct scatterlist *crypt = req->src;
23462306a36Sopenharmony_ci	int err;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if (rctx->cryptlen == req->cryptlen) /* encrypting */
23762306a36Sopenharmony_ci		crypt = req->dst;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	crypt = scatterwalk_ffwd(rctx->src, crypt, req->assoclen);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	ahash_request_set_callback(&preq->req, rctx->flags,
24262306a36Sopenharmony_ci				   poly_cipher_done, req);
24362306a36Sopenharmony_ci	ahash_request_set_tfm(&preq->req, ctx->poly);
24462306a36Sopenharmony_ci	ahash_request_set_crypt(&preq->req, crypt, NULL, rctx->cryptlen);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	err = crypto_ahash_update(&preq->req);
24762306a36Sopenharmony_ci	if (err)
24862306a36Sopenharmony_ci		return err;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	return poly_cipherpad(req);
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic void poly_adpad_done(void *data, int err)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	async_done_continue(data, err, poly_cipher);
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic int poly_adpad(struct aead_request *req)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
26162306a36Sopenharmony_ci	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
26262306a36Sopenharmony_ci	struct poly_req *preq = &rctx->u.poly;
26362306a36Sopenharmony_ci	unsigned int padlen;
26462306a36Sopenharmony_ci	int err;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	padlen = -rctx->assoclen % POLY1305_BLOCK_SIZE;
26762306a36Sopenharmony_ci	memset(preq->pad, 0, sizeof(preq->pad));
26862306a36Sopenharmony_ci	sg_init_one(preq->src, preq->pad, padlen);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	ahash_request_set_callback(&preq->req, rctx->flags,
27162306a36Sopenharmony_ci				   poly_adpad_done, req);
27262306a36Sopenharmony_ci	ahash_request_set_tfm(&preq->req, ctx->poly);
27362306a36Sopenharmony_ci	ahash_request_set_crypt(&preq->req, preq->src, NULL, padlen);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	err = crypto_ahash_update(&preq->req);
27662306a36Sopenharmony_ci	if (err)
27762306a36Sopenharmony_ci		return err;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return poly_cipher(req);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic void poly_ad_done(void *data, int err)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	async_done_continue(data, err, poly_adpad);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic int poly_ad(struct aead_request *req)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
29062306a36Sopenharmony_ci	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
29162306a36Sopenharmony_ci	struct poly_req *preq = &rctx->u.poly;
29262306a36Sopenharmony_ci	int err;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	ahash_request_set_callback(&preq->req, rctx->flags,
29562306a36Sopenharmony_ci				   poly_ad_done, req);
29662306a36Sopenharmony_ci	ahash_request_set_tfm(&preq->req, ctx->poly);
29762306a36Sopenharmony_ci	ahash_request_set_crypt(&preq->req, req->src, NULL, rctx->assoclen);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	err = crypto_ahash_update(&preq->req);
30062306a36Sopenharmony_ci	if (err)
30162306a36Sopenharmony_ci		return err;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return poly_adpad(req);
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic void poly_setkey_done(void *data, int err)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	async_done_continue(data, err, poly_ad);
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic int poly_setkey(struct aead_request *req)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
31462306a36Sopenharmony_ci	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
31562306a36Sopenharmony_ci	struct poly_req *preq = &rctx->u.poly;
31662306a36Sopenharmony_ci	int err;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	sg_init_one(preq->src, rctx->key, sizeof(rctx->key));
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	ahash_request_set_callback(&preq->req, rctx->flags,
32162306a36Sopenharmony_ci				   poly_setkey_done, req);
32262306a36Sopenharmony_ci	ahash_request_set_tfm(&preq->req, ctx->poly);
32362306a36Sopenharmony_ci	ahash_request_set_crypt(&preq->req, preq->src, NULL, sizeof(rctx->key));
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	err = crypto_ahash_update(&preq->req);
32662306a36Sopenharmony_ci	if (err)
32762306a36Sopenharmony_ci		return err;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return poly_ad(req);
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic void poly_init_done(void *data, int err)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	async_done_continue(data, err, poly_setkey);
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic int poly_init(struct aead_request *req)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
34062306a36Sopenharmony_ci	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
34162306a36Sopenharmony_ci	struct poly_req *preq = &rctx->u.poly;
34262306a36Sopenharmony_ci	int err;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	ahash_request_set_callback(&preq->req, rctx->flags,
34562306a36Sopenharmony_ci				   poly_init_done, req);
34662306a36Sopenharmony_ci	ahash_request_set_tfm(&preq->req, ctx->poly);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	err = crypto_ahash_init(&preq->req);
34962306a36Sopenharmony_ci	if (err)
35062306a36Sopenharmony_ci		return err;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	return poly_setkey(req);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic void poly_genkey_done(void *data, int err)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	async_done_continue(data, err, poly_init);
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic int poly_genkey(struct aead_request *req)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
36362306a36Sopenharmony_ci	struct chachapoly_ctx *ctx = crypto_aead_ctx(tfm);
36462306a36Sopenharmony_ci	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
36562306a36Sopenharmony_ci	struct chacha_req *creq = &rctx->u.chacha;
36662306a36Sopenharmony_ci	int err;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	rctx->assoclen = req->assoclen;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (crypto_aead_ivsize(tfm) == 8) {
37162306a36Sopenharmony_ci		if (rctx->assoclen < 8)
37262306a36Sopenharmony_ci			return -EINVAL;
37362306a36Sopenharmony_ci		rctx->assoclen -= 8;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	memset(rctx->key, 0, sizeof(rctx->key));
37762306a36Sopenharmony_ci	sg_init_one(creq->src, rctx->key, sizeof(rctx->key));
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	chacha_iv(creq->iv, req, 0);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	skcipher_request_set_callback(&creq->req, rctx->flags,
38262306a36Sopenharmony_ci				      poly_genkey_done, req);
38362306a36Sopenharmony_ci	skcipher_request_set_tfm(&creq->req, ctx->chacha);
38462306a36Sopenharmony_ci	skcipher_request_set_crypt(&creq->req, creq->src, creq->src,
38562306a36Sopenharmony_ci				   POLY1305_KEY_SIZE, creq->iv);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	err = crypto_skcipher_decrypt(&creq->req);
38862306a36Sopenharmony_ci	if (err)
38962306a36Sopenharmony_ci		return err;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	return poly_init(req);
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic void chacha_encrypt_done(void *data, int err)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	async_done_continue(data, err, poly_genkey);
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic int chacha_encrypt(struct aead_request *req)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
40262306a36Sopenharmony_ci	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
40362306a36Sopenharmony_ci	struct chacha_req *creq = &rctx->u.chacha;
40462306a36Sopenharmony_ci	struct scatterlist *src, *dst;
40562306a36Sopenharmony_ci	int err;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (req->cryptlen == 0)
40862306a36Sopenharmony_ci		goto skip;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	chacha_iv(creq->iv, req, 1);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	src = scatterwalk_ffwd(rctx->src, req->src, req->assoclen);
41362306a36Sopenharmony_ci	dst = src;
41462306a36Sopenharmony_ci	if (req->src != req->dst)
41562306a36Sopenharmony_ci		dst = scatterwalk_ffwd(rctx->dst, req->dst, req->assoclen);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	skcipher_request_set_callback(&creq->req, rctx->flags,
41862306a36Sopenharmony_ci				      chacha_encrypt_done, req);
41962306a36Sopenharmony_ci	skcipher_request_set_tfm(&creq->req, ctx->chacha);
42062306a36Sopenharmony_ci	skcipher_request_set_crypt(&creq->req, src, dst,
42162306a36Sopenharmony_ci				   req->cryptlen, creq->iv);
42262306a36Sopenharmony_ci	err = crypto_skcipher_encrypt(&creq->req);
42362306a36Sopenharmony_ci	if (err)
42462306a36Sopenharmony_ci		return err;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ciskip:
42762306a36Sopenharmony_ci	return poly_genkey(req);
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic int chachapoly_encrypt(struct aead_request *req)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	rctx->cryptlen = req->cryptlen;
43562306a36Sopenharmony_ci	rctx->flags = aead_request_flags(req);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	/* encrypt call chain:
43862306a36Sopenharmony_ci	 * - chacha_encrypt/done()
43962306a36Sopenharmony_ci	 * - poly_genkey/done()
44062306a36Sopenharmony_ci	 * - poly_init/done()
44162306a36Sopenharmony_ci	 * - poly_setkey/done()
44262306a36Sopenharmony_ci	 * - poly_ad/done()
44362306a36Sopenharmony_ci	 * - poly_adpad/done()
44462306a36Sopenharmony_ci	 * - poly_cipher/done()
44562306a36Sopenharmony_ci	 * - poly_cipherpad/done()
44662306a36Sopenharmony_ci	 * - poly_tail/done/continue()
44762306a36Sopenharmony_ci	 * - poly_copy_tag()
44862306a36Sopenharmony_ci	 */
44962306a36Sopenharmony_ci	return chacha_encrypt(req);
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int chachapoly_decrypt(struct aead_request *req)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	rctx->cryptlen = req->cryptlen - POLY1305_DIGEST_SIZE;
45762306a36Sopenharmony_ci	rctx->flags = aead_request_flags(req);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/* decrypt call chain:
46062306a36Sopenharmony_ci	 * - poly_genkey/done()
46162306a36Sopenharmony_ci	 * - poly_init/done()
46262306a36Sopenharmony_ci	 * - poly_setkey/done()
46362306a36Sopenharmony_ci	 * - poly_ad/done()
46462306a36Sopenharmony_ci	 * - poly_adpad/done()
46562306a36Sopenharmony_ci	 * - poly_cipher/done()
46662306a36Sopenharmony_ci	 * - poly_cipherpad/done()
46762306a36Sopenharmony_ci	 * - poly_tail/done/continue()
46862306a36Sopenharmony_ci	 * - chacha_decrypt/done()
46962306a36Sopenharmony_ci	 * - poly_verify_tag()
47062306a36Sopenharmony_ci	 */
47162306a36Sopenharmony_ci	return poly_genkey(req);
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic int chachapoly_setkey(struct crypto_aead *aead, const u8 *key,
47562306a36Sopenharmony_ci			     unsigned int keylen)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct chachapoly_ctx *ctx = crypto_aead_ctx(aead);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (keylen != ctx->saltlen + CHACHA_KEY_SIZE)
48062306a36Sopenharmony_ci		return -EINVAL;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	keylen -= ctx->saltlen;
48362306a36Sopenharmony_ci	memcpy(ctx->salt, key + keylen, ctx->saltlen);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	crypto_skcipher_clear_flags(ctx->chacha, CRYPTO_TFM_REQ_MASK);
48662306a36Sopenharmony_ci	crypto_skcipher_set_flags(ctx->chacha, crypto_aead_get_flags(aead) &
48762306a36Sopenharmony_ci					       CRYPTO_TFM_REQ_MASK);
48862306a36Sopenharmony_ci	return crypto_skcipher_setkey(ctx->chacha, key, keylen);
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic int chachapoly_setauthsize(struct crypto_aead *tfm,
49262306a36Sopenharmony_ci				  unsigned int authsize)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	if (authsize != POLY1305_DIGEST_SIZE)
49562306a36Sopenharmony_ci		return -EINVAL;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return 0;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic int chachapoly_init(struct crypto_aead *tfm)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	struct aead_instance *inst = aead_alg_instance(tfm);
50362306a36Sopenharmony_ci	struct chachapoly_instance_ctx *ictx = aead_instance_ctx(inst);
50462306a36Sopenharmony_ci	struct chachapoly_ctx *ctx = crypto_aead_ctx(tfm);
50562306a36Sopenharmony_ci	struct crypto_skcipher *chacha;
50662306a36Sopenharmony_ci	struct crypto_ahash *poly;
50762306a36Sopenharmony_ci	unsigned long align;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	poly = crypto_spawn_ahash(&ictx->poly);
51062306a36Sopenharmony_ci	if (IS_ERR(poly))
51162306a36Sopenharmony_ci		return PTR_ERR(poly);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	chacha = crypto_spawn_skcipher(&ictx->chacha);
51462306a36Sopenharmony_ci	if (IS_ERR(chacha)) {
51562306a36Sopenharmony_ci		crypto_free_ahash(poly);
51662306a36Sopenharmony_ci		return PTR_ERR(chacha);
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	ctx->chacha = chacha;
52062306a36Sopenharmony_ci	ctx->poly = poly;
52162306a36Sopenharmony_ci	ctx->saltlen = ictx->saltlen;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	align = crypto_aead_alignmask(tfm);
52462306a36Sopenharmony_ci	align &= ~(crypto_tfm_ctx_alignment() - 1);
52562306a36Sopenharmony_ci	crypto_aead_set_reqsize(
52662306a36Sopenharmony_ci		tfm,
52762306a36Sopenharmony_ci		align + offsetof(struct chachapoly_req_ctx, u) +
52862306a36Sopenharmony_ci		max(offsetof(struct chacha_req, req) +
52962306a36Sopenharmony_ci		    sizeof(struct skcipher_request) +
53062306a36Sopenharmony_ci		    crypto_skcipher_reqsize(chacha),
53162306a36Sopenharmony_ci		    offsetof(struct poly_req, req) +
53262306a36Sopenharmony_ci		    sizeof(struct ahash_request) +
53362306a36Sopenharmony_ci		    crypto_ahash_reqsize(poly)));
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	return 0;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic void chachapoly_exit(struct crypto_aead *tfm)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct chachapoly_ctx *ctx = crypto_aead_ctx(tfm);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	crypto_free_ahash(ctx->poly);
54362306a36Sopenharmony_ci	crypto_free_skcipher(ctx->chacha);
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic void chachapoly_free(struct aead_instance *inst)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	struct chachapoly_instance_ctx *ctx = aead_instance_ctx(inst);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	crypto_drop_skcipher(&ctx->chacha);
55162306a36Sopenharmony_ci	crypto_drop_ahash(&ctx->poly);
55262306a36Sopenharmony_ci	kfree(inst);
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic int chachapoly_create(struct crypto_template *tmpl, struct rtattr **tb,
55662306a36Sopenharmony_ci			     const char *name, unsigned int ivsize)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	u32 mask;
55962306a36Sopenharmony_ci	struct aead_instance *inst;
56062306a36Sopenharmony_ci	struct chachapoly_instance_ctx *ctx;
56162306a36Sopenharmony_ci	struct skcipher_alg *chacha;
56262306a36Sopenharmony_ci	struct hash_alg_common *poly;
56362306a36Sopenharmony_ci	int err;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	if (ivsize > CHACHAPOLY_IV_SIZE)
56662306a36Sopenharmony_ci		return -EINVAL;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_AEAD, &mask);
56962306a36Sopenharmony_ci	if (err)
57062306a36Sopenharmony_ci		return err;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
57362306a36Sopenharmony_ci	if (!inst)
57462306a36Sopenharmony_ci		return -ENOMEM;
57562306a36Sopenharmony_ci	ctx = aead_instance_ctx(inst);
57662306a36Sopenharmony_ci	ctx->saltlen = CHACHAPOLY_IV_SIZE - ivsize;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	err = crypto_grab_skcipher(&ctx->chacha, aead_crypto_instance(inst),
57962306a36Sopenharmony_ci				   crypto_attr_alg_name(tb[1]), 0, mask);
58062306a36Sopenharmony_ci	if (err)
58162306a36Sopenharmony_ci		goto err_free_inst;
58262306a36Sopenharmony_ci	chacha = crypto_spawn_skcipher_alg(&ctx->chacha);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	err = crypto_grab_ahash(&ctx->poly, aead_crypto_instance(inst),
58562306a36Sopenharmony_ci				crypto_attr_alg_name(tb[2]), 0, mask);
58662306a36Sopenharmony_ci	if (err)
58762306a36Sopenharmony_ci		goto err_free_inst;
58862306a36Sopenharmony_ci	poly = crypto_spawn_ahash_alg(&ctx->poly);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	err = -EINVAL;
59162306a36Sopenharmony_ci	if (poly->digestsize != POLY1305_DIGEST_SIZE)
59262306a36Sopenharmony_ci		goto err_free_inst;
59362306a36Sopenharmony_ci	/* Need 16-byte IV size, including Initial Block Counter value */
59462306a36Sopenharmony_ci	if (crypto_skcipher_alg_ivsize(chacha) != CHACHA_IV_SIZE)
59562306a36Sopenharmony_ci		goto err_free_inst;
59662306a36Sopenharmony_ci	/* Not a stream cipher? */
59762306a36Sopenharmony_ci	if (chacha->base.cra_blocksize != 1)
59862306a36Sopenharmony_ci		goto err_free_inst;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	err = -ENAMETOOLONG;
60162306a36Sopenharmony_ci	if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME,
60262306a36Sopenharmony_ci		     "%s(%s,%s)", name, chacha->base.cra_name,
60362306a36Sopenharmony_ci		     poly->base.cra_name) >= CRYPTO_MAX_ALG_NAME)
60462306a36Sopenharmony_ci		goto err_free_inst;
60562306a36Sopenharmony_ci	if (snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,
60662306a36Sopenharmony_ci		     "%s(%s,%s)", name, chacha->base.cra_driver_name,
60762306a36Sopenharmony_ci		     poly->base.cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
60862306a36Sopenharmony_ci		goto err_free_inst;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	inst->alg.base.cra_priority = (chacha->base.cra_priority +
61162306a36Sopenharmony_ci				       poly->base.cra_priority) / 2;
61262306a36Sopenharmony_ci	inst->alg.base.cra_blocksize = 1;
61362306a36Sopenharmony_ci	inst->alg.base.cra_alignmask = chacha->base.cra_alignmask |
61462306a36Sopenharmony_ci				       poly->base.cra_alignmask;
61562306a36Sopenharmony_ci	inst->alg.base.cra_ctxsize = sizeof(struct chachapoly_ctx) +
61662306a36Sopenharmony_ci				     ctx->saltlen;
61762306a36Sopenharmony_ci	inst->alg.ivsize = ivsize;
61862306a36Sopenharmony_ci	inst->alg.chunksize = crypto_skcipher_alg_chunksize(chacha);
61962306a36Sopenharmony_ci	inst->alg.maxauthsize = POLY1305_DIGEST_SIZE;
62062306a36Sopenharmony_ci	inst->alg.init = chachapoly_init;
62162306a36Sopenharmony_ci	inst->alg.exit = chachapoly_exit;
62262306a36Sopenharmony_ci	inst->alg.encrypt = chachapoly_encrypt;
62362306a36Sopenharmony_ci	inst->alg.decrypt = chachapoly_decrypt;
62462306a36Sopenharmony_ci	inst->alg.setkey = chachapoly_setkey;
62562306a36Sopenharmony_ci	inst->alg.setauthsize = chachapoly_setauthsize;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	inst->free = chachapoly_free;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	err = aead_register_instance(tmpl, inst);
63062306a36Sopenharmony_ci	if (err) {
63162306a36Sopenharmony_cierr_free_inst:
63262306a36Sopenharmony_ci		chachapoly_free(inst);
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci	return err;
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cistatic int rfc7539_create(struct crypto_template *tmpl, struct rtattr **tb)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	return chachapoly_create(tmpl, tb, "rfc7539", 12);
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic int rfc7539esp_create(struct crypto_template *tmpl, struct rtattr **tb)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	return chachapoly_create(tmpl, tb, "rfc7539esp", 8);
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic struct crypto_template rfc7539_tmpls[] = {
64862306a36Sopenharmony_ci	{
64962306a36Sopenharmony_ci		.name = "rfc7539",
65062306a36Sopenharmony_ci		.create = rfc7539_create,
65162306a36Sopenharmony_ci		.module = THIS_MODULE,
65262306a36Sopenharmony_ci	}, {
65362306a36Sopenharmony_ci		.name = "rfc7539esp",
65462306a36Sopenharmony_ci		.create = rfc7539esp_create,
65562306a36Sopenharmony_ci		.module = THIS_MODULE,
65662306a36Sopenharmony_ci	},
65762306a36Sopenharmony_ci};
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic int __init chacha20poly1305_module_init(void)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	return crypto_register_templates(rfc7539_tmpls,
66262306a36Sopenharmony_ci					 ARRAY_SIZE(rfc7539_tmpls));
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic void __exit chacha20poly1305_module_exit(void)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	crypto_unregister_templates(rfc7539_tmpls,
66862306a36Sopenharmony_ci				    ARRAY_SIZE(rfc7539_tmpls));
66962306a36Sopenharmony_ci}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cisubsys_initcall(chacha20poly1305_module_init);
67262306a36Sopenharmony_cimodule_exit(chacha20poly1305_module_exit);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
67562306a36Sopenharmony_ciMODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
67662306a36Sopenharmony_ciMODULE_DESCRIPTION("ChaCha20-Poly1305 AEAD");
67762306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("rfc7539");
67862306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("rfc7539esp");
679