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
chacha_setkey(struct crypto_skcipher * tfm,const u8 * key,unsigned int keysize,int nrounds)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
chacha20_setkey(struct crypto_skcipher * tfm,const u8 * key,unsigned int keysize)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
chacha12_setkey(struct crypto_skcipher * tfm,const u8 * key,unsigned int keysize)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
chacha_stream_xor(struct skcipher_request * req,const struct chacha_ctx * ctx,const u8 iv[CHACHA_IV_SIZE],bool arch)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
crypto_chacha_crypt_generic(struct skcipher_request * req)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
crypto_chacha_crypt_arch(struct skcipher_request * req)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
crypto_xchacha_crypt(struct skcipher_request * req,bool arch)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
crypto_xchacha_crypt_generic(struct skcipher_request * req)119 static int crypto_xchacha_crypt_generic(struct skcipher_request *req)
120 {
121 return crypto_xchacha_crypt(req, false);
122 }
123
crypto_xchacha_crypt_arch(struct skcipher_request * req)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
crypto_chacha_mod_init(void)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
crypto_chacha_mod_fini(void)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