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])48 static int chacha_stream_xor(struct skcipher_request *req,
49 const struct chacha_ctx *ctx,
50 const u8 iv[CHACHA_IV_SIZE])
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 chacha_crypt(&state, walk.dst.virt.addr, walk.src.virt.addr,
67 nbytes, ctx->nrounds);
68 err = skcipher_walk_done(&walk, walk.nbytes - nbytes);
69 }
70
71 return err;
72 }
73
crypto_chacha_crypt(struct skcipher_request * req)74 static int crypto_chacha_crypt(struct skcipher_request *req)
75 {
76 struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
77 const struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm);
78
79 return chacha_stream_xor(req, ctx, req->iv);
80 }
81
crypto_xchacha_crypt(struct skcipher_request * req)82 static int crypto_xchacha_crypt(struct skcipher_request *req)
83 {
84 struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
85 const struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm);
86 struct chacha_ctx subctx;
87 struct chacha_state state;
88 u8 real_iv[16];
89
90 /* Compute the subkey given the original key and first 128 nonce bits */
91 chacha_init(&state, ctx->key, req->iv);
92 hchacha_block(&state, subctx.key, ctx->nrounds);
93 subctx.nrounds = ctx->nrounds;
94
95 /* Build the real IV */
96 memcpy(&real_iv[0], req->iv + 24, 8); /* stream position */
97 memcpy(&real_iv[8], req->iv + 16, 8); /* remaining 64 nonce bits */
98
99 /* Generate the stream and XOR it with the data */
100 return chacha_stream_xor(req, &subctx, real_iv);
101 }
102
103 static struct skcipher_alg algs[] = {
104 {
105 .base.cra_name = "chacha20",
106 .base.cra_driver_name = "chacha20-lib",
107 .base.cra_priority = 300,
108 .base.cra_blocksize = 1,
109 .base.cra_ctxsize = sizeof(struct chacha_ctx),
110 .base.cra_module = THIS_MODULE,
111
112 .min_keysize = CHACHA_KEY_SIZE,
113 .max_keysize = CHACHA_KEY_SIZE,
114 .ivsize = CHACHA_IV_SIZE,
115 .chunksize = CHACHA_BLOCK_SIZE,
116 .setkey = chacha20_setkey,
117 .encrypt = crypto_chacha_crypt,
118 .decrypt = crypto_chacha_crypt,
119 },
120 {
121 .base.cra_name = "xchacha20",
122 .base.cra_driver_name = "xchacha20-lib",
123 .base.cra_priority = 300,
124 .base.cra_blocksize = 1,
125 .base.cra_ctxsize = sizeof(struct chacha_ctx),
126 .base.cra_module = THIS_MODULE,
127
128 .min_keysize = CHACHA_KEY_SIZE,
129 .max_keysize = CHACHA_KEY_SIZE,
130 .ivsize = XCHACHA_IV_SIZE,
131 .chunksize = CHACHA_BLOCK_SIZE,
132 .setkey = chacha20_setkey,
133 .encrypt = crypto_xchacha_crypt,
134 .decrypt = crypto_xchacha_crypt,
135 },
136 {
137 .base.cra_name = "xchacha12",
138 .base.cra_driver_name = "xchacha12-lib",
139 .base.cra_priority = 300,
140 .base.cra_blocksize = 1,
141 .base.cra_ctxsize = sizeof(struct chacha_ctx),
142 .base.cra_module = THIS_MODULE,
143
144 .min_keysize = CHACHA_KEY_SIZE,
145 .max_keysize = CHACHA_KEY_SIZE,
146 .ivsize = XCHACHA_IV_SIZE,
147 .chunksize = CHACHA_BLOCK_SIZE,
148 .setkey = chacha12_setkey,
149 .encrypt = crypto_xchacha_crypt,
150 .decrypt = crypto_xchacha_crypt,
151 }
152 };
153
crypto_chacha_mod_init(void)154 static int __init crypto_chacha_mod_init(void)
155 {
156 return crypto_register_skciphers(algs, ARRAY_SIZE(algs));
157 }
158
crypto_chacha_mod_fini(void)159 static void __exit crypto_chacha_mod_fini(void)
160 {
161 crypto_unregister_skciphers(algs, ARRAY_SIZE(algs));
162 }
163
164 module_init(crypto_chacha_mod_init);
165 module_exit(crypto_chacha_mod_fini);
166
167 MODULE_LICENSE("GPL");
168 MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
169 MODULE_DESCRIPTION("Crypto API wrappers for the ChaCha20, XChaCha20, and XChaCha12 stream ciphers");
170 MODULE_ALIAS_CRYPTO("chacha20");
171 MODULE_ALIAS_CRYPTO("chacha20-lib");
172 MODULE_ALIAS_CRYPTO("xchacha20");
173 MODULE_ALIAS_CRYPTO("xchacha20-lib");
174 MODULE_ALIAS_CRYPTO("xchacha12");
175 MODULE_ALIAS_CRYPTO("xchacha12-lib");
176