12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2a10f554fSHerbert Xu /*
3a10f554fSHerbert Xu * echainiv: Encrypted Chain IV Generator
4a10f554fSHerbert Xu *
553a5d5ddSHerbert Xu * This generator generates an IV based on a sequence number by multiplying
653a5d5ddSHerbert Xu * it with a salt and then encrypting it with the same key as used to encrypt
7a10f554fSHerbert Xu * the plain text. This algorithm requires that the block size be equal
8a10f554fSHerbert Xu * to the IV size. It is mainly useful for CBC.
9a10f554fSHerbert Xu *
10a10f554fSHerbert Xu * This generator can only be used by algorithms where authentication
11a10f554fSHerbert Xu * is performed after encryption (i.e., authenc).
12a10f554fSHerbert Xu *
13a10f554fSHerbert Xu * Copyright (c) 2015 Herbert Xu <herbert@gondor.apana.org.au>
14a10f554fSHerbert Xu */
15a10f554fSHerbert Xu
16d97de47cSHerbert Xu #include <crypto/internal/geniv.h>
17a10f554fSHerbert Xu #include <crypto/scatterwalk.h>
180e8bff47SHerbert Xu #include <crypto/skcipher.h>
19a10f554fSHerbert Xu #include <linux/err.h>
20a10f554fSHerbert Xu #include <linux/init.h>
21a10f554fSHerbert Xu #include <linux/kernel.h>
22a10f554fSHerbert Xu #include <linux/module.h>
2353a5d5ddSHerbert Xu #include <linux/slab.h>
24a10f554fSHerbert Xu #include <linux/string.h>
25a10f554fSHerbert Xu
echainiv_encrypt(struct aead_request * req)26a10f554fSHerbert Xu static int echainiv_encrypt(struct aead_request *req)
27a10f554fSHerbert Xu {
28a10f554fSHerbert Xu struct crypto_aead *geniv = crypto_aead_reqtfm(req);
29376e0d69SHerbert Xu struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
30a10f554fSHerbert Xu struct aead_request *subreq = aead_request_ctx(req);
3153a5d5ddSHerbert Xu __be64 nseqno;
3253a5d5ddSHerbert Xu u64 seqno;
33a10f554fSHerbert Xu u8 *info;
34823655c9SHerbert Xu unsigned int ivsize = crypto_aead_ivsize(geniv);
35a10f554fSHerbert Xu int err;
36a10f554fSHerbert Xu
37823655c9SHerbert Xu if (req->cryptlen < ivsize)
38823655c9SHerbert Xu return -EINVAL;
39823655c9SHerbert Xu
40376e0d69SHerbert Xu aead_request_set_tfm(subreq, ctx->child);
41a10f554fSHerbert Xu
42a10f554fSHerbert Xu info = req->iv;
43a10f554fSHerbert Xu
44a10f554fSHerbert Xu if (req->src != req->dst) {
458d605398SKees Cook SYNC_SKCIPHER_REQUEST_ON_STACK(nreq, ctx->sknull);
46a10f554fSHerbert Xu
478d605398SKees Cook skcipher_request_set_sync_tfm(nreq, ctx->sknull);
480e8bff47SHerbert Xu skcipher_request_set_callback(nreq, req->base.flags,
490e8bff47SHerbert Xu NULL, NULL);
500e8bff47SHerbert Xu skcipher_request_set_crypt(nreq, req->src, req->dst,
510e8bff47SHerbert Xu req->assoclen + req->cryptlen,
520e8bff47SHerbert Xu NULL);
530e8bff47SHerbert Xu
540e8bff47SHerbert Xu err = crypto_skcipher_encrypt(nreq);
55a10f554fSHerbert Xu if (err)
56a10f554fSHerbert Xu return err;
57a10f554fSHerbert Xu }
58a10f554fSHerbert Xu
5953a5d5ddSHerbert Xu aead_request_set_callback(subreq, req->base.flags,
6053a5d5ddSHerbert Xu req->base.complete, req->base.data);
61a10f554fSHerbert Xu aead_request_set_crypt(subreq, req->dst, req->dst,
625499b1a7SHerbert Xu req->cryptlen, info);
635499b1a7SHerbert Xu aead_request_set_ad(subreq, req->assoclen);
64a10f554fSHerbert Xu
6553a5d5ddSHerbert Xu memcpy(&nseqno, info + ivsize - 8, 8);
6653a5d5ddSHerbert Xu seqno = be64_to_cpu(nseqno);
6753a5d5ddSHerbert Xu memset(info, 0, ivsize);
68a10f554fSHerbert Xu
6953a5d5ddSHerbert Xu scatterwalk_map_and_copy(info, req->dst, req->assoclen, ivsize, 1);
7053a5d5ddSHerbert Xu
7153a5d5ddSHerbert Xu do {
7253a5d5ddSHerbert Xu u64 a;
7353a5d5ddSHerbert Xu
7453a5d5ddSHerbert Xu memcpy(&a, ctx->salt + ivsize - 8, 8);
7553a5d5ddSHerbert Xu
7653a5d5ddSHerbert Xu a |= 1;
7753a5d5ddSHerbert Xu a *= seqno;
7853a5d5ddSHerbert Xu
7953a5d5ddSHerbert Xu memcpy(info + ivsize - 8, &a, 8);
8053a5d5ddSHerbert Xu } while ((ivsize -= 8));
8153a5d5ddSHerbert Xu
8253a5d5ddSHerbert Xu return crypto_aead_encrypt(subreq);
83a10f554fSHerbert Xu }
84a10f554fSHerbert Xu
echainiv_decrypt(struct aead_request * req)85a10f554fSHerbert Xu static int echainiv_decrypt(struct aead_request *req)
86a10f554fSHerbert Xu {
87a10f554fSHerbert Xu struct crypto_aead *geniv = crypto_aead_reqtfm(req);
88376e0d69SHerbert Xu struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
89a10f554fSHerbert Xu struct aead_request *subreq = aead_request_ctx(req);
90a10f554fSHerbert Xu crypto_completion_t compl;
91a10f554fSHerbert Xu void *data;
92823655c9SHerbert Xu unsigned int ivsize = crypto_aead_ivsize(geniv);
93823655c9SHerbert Xu
945499b1a7SHerbert Xu if (req->cryptlen < ivsize)
95823655c9SHerbert Xu return -EINVAL;
96a10f554fSHerbert Xu
97376e0d69SHerbert Xu aead_request_set_tfm(subreq, ctx->child);
98a10f554fSHerbert Xu
99a10f554fSHerbert Xu compl = req->base.complete;
100a10f554fSHerbert Xu data = req->base.data;
101a10f554fSHerbert Xu
102a10f554fSHerbert Xu aead_request_set_callback(subreq, req->base.flags, compl, data);
103a10f554fSHerbert Xu aead_request_set_crypt(subreq, req->src, req->dst,
104a10f554fSHerbert Xu req->cryptlen - ivsize, req->iv);
105374d4ad1SHerbert Xu aead_request_set_ad(subreq, req->assoclen + ivsize);
106a10f554fSHerbert Xu
107a10f554fSHerbert Xu scatterwalk_map_and_copy(req->iv, req->src, req->assoclen, ivsize, 0);
108a10f554fSHerbert Xu
109a10f554fSHerbert Xu return crypto_aead_decrypt(subreq);
110a10f554fSHerbert Xu }
111a10f554fSHerbert Xu
echainiv_aead_create(struct crypto_template * tmpl,struct rtattr ** tb)1121e419c79SHerbert Xu static int echainiv_aead_create(struct crypto_template *tmpl,
1131e419c79SHerbert Xu struct rtattr **tb)
114a10f554fSHerbert Xu {
115a10f554fSHerbert Xu struct aead_instance *inst;
1161e419c79SHerbert Xu int err;
117a10f554fSHerbert Xu
118*e72b48c5SEric Biggers inst = aead_geniv_alloc(tmpl, tb);
119a10f554fSHerbert Xu
120a10f554fSHerbert Xu if (IS_ERR(inst))
1211e419c79SHerbert Xu return PTR_ERR(inst);
122a10f554fSHerbert Xu
123d97de47cSHerbert Xu err = -EINVAL;
12453a5d5ddSHerbert Xu if (inst->alg.ivsize & (sizeof(u64) - 1) || !inst->alg.ivsize)
125d97de47cSHerbert Xu goto free_inst;
126d97de47cSHerbert Xu
127f261c5fbSHerbert Xu inst->alg.encrypt = echainiv_encrypt;
128a10f554fSHerbert Xu inst->alg.decrypt = echainiv_decrypt;
129a10f554fSHerbert Xu
130376e0d69SHerbert Xu inst->alg.init = aead_init_geniv;
131376e0d69SHerbert Xu inst->alg.exit = aead_exit_geniv;
132a10f554fSHerbert Xu
133376e0d69SHerbert Xu inst->alg.base.cra_ctxsize = sizeof(struct aead_geniv_ctx);
1349d03aee1SHerbert Xu inst->alg.base.cra_ctxsize += inst->alg.ivsize;
135a10f554fSHerbert Xu
1361e419c79SHerbert Xu err = aead_register_instance(tmpl, inst);
1370f8f6d86SEric Biggers if (err) {
1381e419c79SHerbert Xu free_inst:
1390f8f6d86SEric Biggers inst->free(inst);
140a10f554fSHerbert Xu }
1410f8f6d86SEric Biggers return err;
142a10f554fSHerbert Xu }
143a10f554fSHerbert Xu
144a10f554fSHerbert Xu static struct crypto_template echainiv_tmpl = {
145a10f554fSHerbert Xu .name = "echainiv",
1469fcc704dSHerbert Xu .create = echainiv_aead_create,
147a10f554fSHerbert Xu .module = THIS_MODULE,
148a10f554fSHerbert Xu };
149a10f554fSHerbert Xu
echainiv_module_init(void)150a10f554fSHerbert Xu static int __init echainiv_module_init(void)
151a10f554fSHerbert Xu {
152a10f554fSHerbert Xu return crypto_register_template(&echainiv_tmpl);
153a10f554fSHerbert Xu }
154a10f554fSHerbert Xu
echainiv_module_exit(void)155a10f554fSHerbert Xu static void __exit echainiv_module_exit(void)
156a10f554fSHerbert Xu {
157a10f554fSHerbert Xu crypto_unregister_template(&echainiv_tmpl);
158a10f554fSHerbert Xu }
159a10f554fSHerbert Xu
160c4741b23SEric Biggers subsys_initcall(echainiv_module_init);
161a10f554fSHerbert Xu module_exit(echainiv_module_exit);
162a10f554fSHerbert Xu
163a10f554fSHerbert Xu MODULE_LICENSE("GPL");
164a10f554fSHerbert Xu MODULE_DESCRIPTION("Encrypted Chain IV Generator");
165a10f554fSHerbert Xu MODULE_ALIAS_CRYPTO("echainiv");
166