1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Crypto API wrappers for the ChaCha20, XChaCha20, and XChaCha12 stream ciphers
4  *
5  * Copyright (C) 2015 Martin Willi
6  * Copyright (C) 2018 Google LLC
7  */
8 
9 #include <linux/unaligned.h>
10 #include <crypto/algapi.h>
11 #include <crypto/chacha.h>
12 #include <crypto/internal/skcipher.h>
13 #include <linux/module.h>
14 
15 struct chacha_ctx {
16 	u32 key[8];
17 	int nrounds;
18 };
19 
20 static int chacha_setkey(struct crypto_skcipher *tfm,
21 			 const u8 *key, unsigned int keysize, int nrounds)
22 {
23 	struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm);
24 	int i;
25 
26 	if (keysize != CHACHA_KEY_SIZE)
27 		return -EINVAL;
28 
29 	for (i = 0; i < ARRAY_SIZE(ctx->key); i++)
30 		ctx->key[i] = get_unaligned_le32(key + i * sizeof(u32));
31 
32 	ctx->nrounds = nrounds;
33 	return 0;
34 }
35 
36 static int chacha20_setkey(struct crypto_skcipher *tfm,
37 			   const u8 *key, unsigned int keysize)
38 {
39 	return chacha_setkey(tfm, key, keysize, 20);
40 }
41 
42 static int chacha12_setkey(struct crypto_skcipher *tfm,
43 			   const u8 *key, unsigned int keysize)
44 {
45 	return chacha_setkey(tfm, key, keysize, 12);
46 }
47 
48 static int chacha_stream_xor(struct skcipher_request *req,
49 			     const struct chacha_ctx *ctx,
50 			     const u8 iv[CHACHA_IV_SIZE], bool arch)
51 {
52 	struct skcipher_walk walk;
53 	struct chacha_state state;
54 	int err;
55 
56 	err = skcipher_walk_virt(&walk, req, false);
57 
58 	chacha_init(&state, ctx->key, iv);
59 
60 	while (walk.nbytes > 0) {
61 		unsigned int nbytes = walk.nbytes;
62 
63 		if (nbytes < walk.total)
64 			nbytes = round_down(nbytes, CHACHA_BLOCK_SIZE);
65 
66 		if (arch)
67 			chacha_crypt(&state, walk.dst.virt.addr,
68 				     walk.src.virt.addr, nbytes, ctx->nrounds);
69 		else
70 			chacha_crypt_generic(&state, walk.dst.virt.addr,
71 					     walk.src.virt.addr, nbytes,
72 					     ctx->nrounds);
73 		err = skcipher_walk_done(&walk, walk.nbytes - nbytes);
74 	}
75 
76 	return err;
77 }
78 
79 static int crypto_chacha_crypt_generic(struct skcipher_request *req)
80 {
81 	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
82 	const struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm);
83 
84 	return chacha_stream_xor(req, ctx, req->iv, false);
85 }
86 
87 static int crypto_chacha_crypt_arch(struct skcipher_request *req)
88 {
89 	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
90 	const struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm);
91 
92 	return chacha_stream_xor(req, ctx, req->iv, true);
93 }
94 
95 static int crypto_xchacha_crypt(struct skcipher_request *req, bool arch)
96 {
97 	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
98 	const struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm);
99 	struct chacha_ctx subctx;
100 	struct chacha_state state;
101 	u8 real_iv[16];
102 
103 	/* Compute the subkey given the original key and first 128 nonce bits */
104 	chacha_init(&state, ctx->key, req->iv);
105 	if (arch)
106 		hchacha_block(&state, subctx.key, ctx->nrounds);
107 	else
108 		hchacha_block_generic(&state, subctx.key, ctx->nrounds);
109 	subctx.nrounds = ctx->nrounds;
110 
111 	/* Build the real IV */
112 	memcpy(&real_iv[0], req->iv + 24, 8); /* stream position */
113 	memcpy(&real_iv[8], req->iv + 16, 8); /* remaining 64 nonce bits */
114 
115 	/* Generate the stream and XOR it with the data */
116 	return chacha_stream_xor(req, &subctx, real_iv, arch);
117 }
118 
119 static int crypto_xchacha_crypt_generic(struct skcipher_request *req)
120 {
121 	return crypto_xchacha_crypt(req, false);
122 }
123 
124 static int crypto_xchacha_crypt_arch(struct skcipher_request *req)
125 {
126 	return crypto_xchacha_crypt(req, true);
127 }
128 
129 static struct skcipher_alg algs[] = {
130 	{
131 		.base.cra_name		= "chacha20",
132 		.base.cra_driver_name	= "chacha20-generic",
133 		.base.cra_priority	= 100,
134 		.base.cra_blocksize	= 1,
135 		.base.cra_ctxsize	= sizeof(struct chacha_ctx),
136 		.base.cra_module	= THIS_MODULE,
137 
138 		.min_keysize		= CHACHA_KEY_SIZE,
139 		.max_keysize		= CHACHA_KEY_SIZE,
140 		.ivsize			= CHACHA_IV_SIZE,
141 		.chunksize		= CHACHA_BLOCK_SIZE,
142 		.setkey			= chacha20_setkey,
143 		.encrypt		= crypto_chacha_crypt_generic,
144 		.decrypt		= crypto_chacha_crypt_generic,
145 	},
146 	{
147 		.base.cra_name		= "xchacha20",
148 		.base.cra_driver_name	= "xchacha20-generic",
149 		.base.cra_priority	= 100,
150 		.base.cra_blocksize	= 1,
151 		.base.cra_ctxsize	= sizeof(struct chacha_ctx),
152 		.base.cra_module	= THIS_MODULE,
153 
154 		.min_keysize		= CHACHA_KEY_SIZE,
155 		.max_keysize		= CHACHA_KEY_SIZE,
156 		.ivsize			= XCHACHA_IV_SIZE,
157 		.chunksize		= CHACHA_BLOCK_SIZE,
158 		.setkey			= chacha20_setkey,
159 		.encrypt		= crypto_xchacha_crypt_generic,
160 		.decrypt		= crypto_xchacha_crypt_generic,
161 	},
162 	{
163 		.base.cra_name		= "xchacha12",
164 		.base.cra_driver_name	= "xchacha12-generic",
165 		.base.cra_priority	= 100,
166 		.base.cra_blocksize	= 1,
167 		.base.cra_ctxsize	= sizeof(struct chacha_ctx),
168 		.base.cra_module	= THIS_MODULE,
169 
170 		.min_keysize		= CHACHA_KEY_SIZE,
171 		.max_keysize		= CHACHA_KEY_SIZE,
172 		.ivsize			= XCHACHA_IV_SIZE,
173 		.chunksize		= CHACHA_BLOCK_SIZE,
174 		.setkey			= chacha12_setkey,
175 		.encrypt		= crypto_xchacha_crypt_generic,
176 		.decrypt		= crypto_xchacha_crypt_generic,
177 	},
178 	{
179 		.base.cra_name		= "chacha20",
180 		.base.cra_driver_name	= "chacha20-" __stringify(ARCH),
181 		.base.cra_priority	= 300,
182 		.base.cra_blocksize	= 1,
183 		.base.cra_ctxsize	= sizeof(struct chacha_ctx),
184 		.base.cra_module	= THIS_MODULE,
185 
186 		.min_keysize		= CHACHA_KEY_SIZE,
187 		.max_keysize		= CHACHA_KEY_SIZE,
188 		.ivsize			= CHACHA_IV_SIZE,
189 		.chunksize		= CHACHA_BLOCK_SIZE,
190 		.setkey			= chacha20_setkey,
191 		.encrypt		= crypto_chacha_crypt_arch,
192 		.decrypt		= crypto_chacha_crypt_arch,
193 	},
194 	{
195 		.base.cra_name		= "xchacha20",
196 		.base.cra_driver_name	= "xchacha20-" __stringify(ARCH),
197 		.base.cra_priority	= 300,
198 		.base.cra_blocksize	= 1,
199 		.base.cra_ctxsize	= sizeof(struct chacha_ctx),
200 		.base.cra_module	= THIS_MODULE,
201 
202 		.min_keysize		= CHACHA_KEY_SIZE,
203 		.max_keysize		= CHACHA_KEY_SIZE,
204 		.ivsize			= XCHACHA_IV_SIZE,
205 		.chunksize		= CHACHA_BLOCK_SIZE,
206 		.setkey			= chacha20_setkey,
207 		.encrypt		= crypto_xchacha_crypt_arch,
208 		.decrypt		= crypto_xchacha_crypt_arch,
209 	},
210 	{
211 		.base.cra_name		= "xchacha12",
212 		.base.cra_driver_name	= "xchacha12-" __stringify(ARCH),
213 		.base.cra_priority	= 300,
214 		.base.cra_blocksize	= 1,
215 		.base.cra_ctxsize	= sizeof(struct chacha_ctx),
216 		.base.cra_module	= THIS_MODULE,
217 
218 		.min_keysize		= CHACHA_KEY_SIZE,
219 		.max_keysize		= CHACHA_KEY_SIZE,
220 		.ivsize			= XCHACHA_IV_SIZE,
221 		.chunksize		= CHACHA_BLOCK_SIZE,
222 		.setkey			= chacha12_setkey,
223 		.encrypt		= crypto_xchacha_crypt_arch,
224 		.decrypt		= crypto_xchacha_crypt_arch,
225 	}
226 };
227 
228 static unsigned int num_algs;
229 
230 static int __init crypto_chacha_mod_init(void)
231 {
232 	/* register the arch flavours only if they differ from generic */
233 	num_algs = ARRAY_SIZE(algs);
234 	BUILD_BUG_ON(ARRAY_SIZE(algs) % 2 != 0);
235 	if (!chacha_is_arch_optimized())
236 		num_algs /= 2;
237 
238 	return crypto_register_skciphers(algs, num_algs);
239 }
240 
241 static void __exit crypto_chacha_mod_fini(void)
242 {
243 	crypto_unregister_skciphers(algs, num_algs);
244 }
245 
246 module_init(crypto_chacha_mod_init);
247 module_exit(crypto_chacha_mod_fini);
248 
249 MODULE_LICENSE("GPL");
250 MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
251 MODULE_DESCRIPTION("Crypto API wrappers for the ChaCha20, XChaCha20, and XChaCha12 stream ciphers");
252 MODULE_ALIAS_CRYPTO("chacha20");
253 MODULE_ALIAS_CRYPTO("chacha20-generic");
254 MODULE_ALIAS_CRYPTO("chacha20-"  __stringify(ARCH));
255 MODULE_ALIAS_CRYPTO("xchacha20");
256 MODULE_ALIAS_CRYPTO("xchacha20-generic");
257 MODULE_ALIAS_CRYPTO("xchacha20-"  __stringify(ARCH));
258 MODULE_ALIAS_CRYPTO("xchacha12");
259 MODULE_ALIAS_CRYPTO("xchacha12-generic");
260 MODULE_ALIAS_CRYPTO("xchacha12-"  __stringify(ARCH));
261