Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * ChaCha and XChaCha stream ciphers, including ChaCha20 (RFC7539)
  4 *
  5 * Copyright (C) 2015 Martin Willi
  6 * Copyright (C) 2018 Google LLC
  7 */
  8
  9#include <asm/unaligned.h>
 10#include <crypto/algapi.h>
 11#include <crypto/chacha.h>
 12#include <crypto/internal/skcipher.h>
 13#include <linux/module.h>
 14
 15static void chacha_docrypt(u32 *state, u8 *dst, const u8 *src,
 16			   unsigned int bytes, int nrounds)
 17{
 18	/* aligned to potentially speed up crypto_xor() */
 19	u8 stream[CHACHA_BLOCK_SIZE] __aligned(sizeof(long));
 20
 21	while (bytes >= CHACHA_BLOCK_SIZE) {
 22		chacha_block(state, stream, nrounds);
 23		crypto_xor_cpy(dst, src, stream, CHACHA_BLOCK_SIZE);
 24		bytes -= CHACHA_BLOCK_SIZE;
 25		dst += CHACHA_BLOCK_SIZE;
 26		src += CHACHA_BLOCK_SIZE;
 27	}
 28	if (bytes) {
 29		chacha_block(state, stream, nrounds);
 30		crypto_xor_cpy(dst, src, stream, bytes);
 31	}
 32}
 33
 34static int chacha_stream_xor(struct skcipher_request *req,
 35			     const struct chacha_ctx *ctx, const u8 *iv)
 36{
 37	struct skcipher_walk walk;
 38	u32 state[16];
 39	int err;
 40
 41	err = skcipher_walk_virt(&walk, req, false);
 42
 43	crypto_chacha_init(state, ctx, iv);
 44
 45	while (walk.nbytes > 0) {
 46		unsigned int nbytes = walk.nbytes;
 47
 48		if (nbytes < walk.total)
 49			nbytes = round_down(nbytes, CHACHA_BLOCK_SIZE);
 50
 51		chacha_docrypt(state, walk.dst.virt.addr, walk.src.virt.addr,
 52			       nbytes, ctx->nrounds);
 53		err = skcipher_walk_done(&walk, walk.nbytes - nbytes);
 54	}
 55
 56	return err;
 57}
 58
 59void crypto_chacha_init(u32 *state, const struct chacha_ctx *ctx, const u8 *iv)
 60{
 61	state[0]  = 0x61707865; /* "expa" */
 62	state[1]  = 0x3320646e; /* "nd 3" */
 63	state[2]  = 0x79622d32; /* "2-by" */
 64	state[3]  = 0x6b206574; /* "te k" */
 65	state[4]  = ctx->key[0];
 66	state[5]  = ctx->key[1];
 67	state[6]  = ctx->key[2];
 68	state[7]  = ctx->key[3];
 69	state[8]  = ctx->key[4];
 70	state[9]  = ctx->key[5];
 71	state[10] = ctx->key[6];
 72	state[11] = ctx->key[7];
 73	state[12] = get_unaligned_le32(iv +  0);
 74	state[13] = get_unaligned_le32(iv +  4);
 75	state[14] = get_unaligned_le32(iv +  8);
 76	state[15] = get_unaligned_le32(iv + 12);
 77}
 78EXPORT_SYMBOL_GPL(crypto_chacha_init);
 79
 80static int chacha_setkey(struct crypto_skcipher *tfm, const u8 *key,
 81			 unsigned int keysize, int nrounds)
 82{
 83	struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm);
 84	int i;
 85
 86	if (keysize != CHACHA_KEY_SIZE)
 87		return -EINVAL;
 88
 89	for (i = 0; i < ARRAY_SIZE(ctx->key); i++)
 90		ctx->key[i] = get_unaligned_le32(key + i * sizeof(u32));
 91
 92	ctx->nrounds = nrounds;
 93	return 0;
 94}
 95
 96int crypto_chacha20_setkey(struct crypto_skcipher *tfm, const u8 *key,
 97			   unsigned int keysize)
 98{
 99	return chacha_setkey(tfm, key, keysize, 20);
100}
101EXPORT_SYMBOL_GPL(crypto_chacha20_setkey);
102
103int crypto_chacha12_setkey(struct crypto_skcipher *tfm, const u8 *key,
104			   unsigned int keysize)
105{
106	return chacha_setkey(tfm, key, keysize, 12);
107}
108EXPORT_SYMBOL_GPL(crypto_chacha12_setkey);
109
110int crypto_chacha_crypt(struct skcipher_request *req)
111{
112	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
113	struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm);
114
115	return chacha_stream_xor(req, ctx, req->iv);
116}
117EXPORT_SYMBOL_GPL(crypto_chacha_crypt);
118
119int crypto_xchacha_crypt(struct skcipher_request *req)
120{
121	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
122	struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm);
123	struct chacha_ctx subctx;
124	u32 state[16];
125	u8 real_iv[16];
126
127	/* Compute the subkey given the original key and first 128 nonce bits */
128	crypto_chacha_init(state, ctx, req->iv);
129	hchacha_block(state, subctx.key, ctx->nrounds);
130	subctx.nrounds = ctx->nrounds;
131
132	/* Build the real IV */
133	memcpy(&real_iv[0], req->iv + 24, 8); /* stream position */
134	memcpy(&real_iv[8], req->iv + 16, 8); /* remaining 64 nonce bits */
135
136	/* Generate the stream and XOR it with the data */
137	return chacha_stream_xor(req, &subctx, real_iv);
138}
139EXPORT_SYMBOL_GPL(crypto_xchacha_crypt);
140
141static struct skcipher_alg algs[] = {
142	{
143		.base.cra_name		= "chacha20",
144		.base.cra_driver_name	= "chacha20-generic",
145		.base.cra_priority	= 100,
146		.base.cra_blocksize	= 1,
147		.base.cra_ctxsize	= sizeof(struct chacha_ctx),
148		.base.cra_module	= THIS_MODULE,
149
150		.min_keysize		= CHACHA_KEY_SIZE,
151		.max_keysize		= CHACHA_KEY_SIZE,
152		.ivsize			= CHACHA_IV_SIZE,
153		.chunksize		= CHACHA_BLOCK_SIZE,
154		.setkey			= crypto_chacha20_setkey,
155		.encrypt		= crypto_chacha_crypt,
156		.decrypt		= crypto_chacha_crypt,
157	}, {
158		.base.cra_name		= "xchacha20",
159		.base.cra_driver_name	= "xchacha20-generic",
160		.base.cra_priority	= 100,
161		.base.cra_blocksize	= 1,
162		.base.cra_ctxsize	= sizeof(struct chacha_ctx),
163		.base.cra_module	= THIS_MODULE,
164
165		.min_keysize		= CHACHA_KEY_SIZE,
166		.max_keysize		= CHACHA_KEY_SIZE,
167		.ivsize			= XCHACHA_IV_SIZE,
168		.chunksize		= CHACHA_BLOCK_SIZE,
169		.setkey			= crypto_chacha20_setkey,
170		.encrypt		= crypto_xchacha_crypt,
171		.decrypt		= crypto_xchacha_crypt,
172	}, {
173		.base.cra_name		= "xchacha12",
174		.base.cra_driver_name	= "xchacha12-generic",
175		.base.cra_priority	= 100,
176		.base.cra_blocksize	= 1,
177		.base.cra_ctxsize	= sizeof(struct chacha_ctx),
178		.base.cra_module	= THIS_MODULE,
179
180		.min_keysize		= CHACHA_KEY_SIZE,
181		.max_keysize		= CHACHA_KEY_SIZE,
182		.ivsize			= XCHACHA_IV_SIZE,
183		.chunksize		= CHACHA_BLOCK_SIZE,
184		.setkey			= crypto_chacha12_setkey,
185		.encrypt		= crypto_xchacha_crypt,
186		.decrypt		= crypto_xchacha_crypt,
187	}
188};
189
190static int __init chacha_generic_mod_init(void)
191{
192	return crypto_register_skciphers(algs, ARRAY_SIZE(algs));
193}
194
195static void __exit chacha_generic_mod_fini(void)
196{
197	crypto_unregister_skciphers(algs, ARRAY_SIZE(algs));
198}
199
200subsys_initcall(chacha_generic_mod_init);
201module_exit(chacha_generic_mod_fini);
202
203MODULE_LICENSE("GPL");
204MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
205MODULE_DESCRIPTION("ChaCha and XChaCha stream ciphers (generic)");
206MODULE_ALIAS_CRYPTO("chacha20");
207MODULE_ALIAS_CRYPTO("chacha20-generic");
208MODULE_ALIAS_CRYPTO("xchacha20");
209MODULE_ALIAS_CRYPTO("xchacha20-generic");
210MODULE_ALIAS_CRYPTO("xchacha12");
211MODULE_ALIAS_CRYPTO("xchacha12-generic");