xref: /openbmc/linux/crypto/echainiv.c (revision e72b48c5)
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 
118e72b48c5SEric 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