xref: /linux/crypto/chacha.c (revision d8768fb12a14c30436bd0466b4fc28edeef45078)
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