Commit de61d7ae authored by Eric Biggers's avatar Eric Biggers Committed by Herbert Xu
Browse files

crypto: chacha20-generic - add XChaCha20 support

Add support for the XChaCha20 stream cipher.  XChaCha20 is the
application of the XSalsa20 construction
(https://cr.yp.to/snuffle/xsalsa-20081128.pdf

) to ChaCha20 rather than
to Salsa20.  XChaCha20 extends ChaCha20's nonce length from 64 bits (or
96 bits, depending on convention) to 192 bits, while provably retaining
ChaCha20's security.  XChaCha20 uses the ChaCha20 permutation to map the
key and first 128 nonce bits to a 256-bit subkey.  Then, it does the
ChaCha20 stream cipher with the subkey and remaining 64 bits of nonce.

We need XChaCha support in order to add support for the Adiantum
encryption mode.  Note that to meet our performance requirements, we
actually plan to primarily use the variant XChaCha12.  But we believe
it's wise to first add XChaCha20 as a baseline with a higher security
margin, in case there are any situations where it can be used.
Supporting both variants is straightforward.

Since XChaCha20's subkey differs for each request, XChaCha20 can't be a
template that wraps ChaCha20; that would require re-keying the
underlying ChaCha20 for every request, which wouldn't be thread-safe.
Instead, we make XChaCha20 its own top-level algorithm which calls the
ChaCha20 streaming implementation internally.

Similar to the existing ChaCha20 implementation, we define the IV to be
the nonce and stream position concatenated together.  This allows users
to seek to any position in the stream.

I considered splitting the code into separate chacha20-common, chacha20,
and xchacha20 modules, so that chacha20 and xchacha20 could be
enabled/disabled independently.  However, since nearly all the code is
shared anyway, I ultimately decided there would have been little benefit
to the added complexity of separate modules.

Reviewed-by: default avatarArd Biesheuvel <ard.biesheuvel@linaro.org>
Acked-by: default avatarMartin Willi <martin@strongswan.org>
Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent 5e04542a
Loading
Loading
Loading
Loading
+9 −5
Original line number Original line Diff line number Diff line
@@ -1403,18 +1403,22 @@ config CRYPTO_SALSA20
	  Bernstein <djb@cr.yp.to>. See <http://cr.yp.to/snuffle.html>
	  Bernstein <djb@cr.yp.to>. See <http://cr.yp.to/snuffle.html>


config CRYPTO_CHACHA20
config CRYPTO_CHACHA20
	tristate "ChaCha20 cipher algorithm"
	tristate "ChaCha20 stream cipher algorithms"
	select CRYPTO_BLKCIPHER
	select CRYPTO_BLKCIPHER
	help
	help
	  ChaCha20 cipher algorithm, RFC7539.
	  The ChaCha20 and XChaCha20 stream cipher algorithms.


	  ChaCha20 is a 256-bit high-speed stream cipher designed by Daniel J.
	  ChaCha20 is a 256-bit high-speed stream cipher designed by Daniel J.
	  Bernstein and further specified in RFC7539 for use in IETF protocols.
	  Bernstein and further specified in RFC7539 for use in IETF protocols.
	  This is the portable C implementation of ChaCha20.
	  This is the portable C implementation of ChaCha20.  See also:

	  See also:
	  <http://cr.yp.to/chacha/chacha-20080128.pdf>
	  <http://cr.yp.to/chacha/chacha-20080128.pdf>


	  XChaCha20 is the application of the XSalsa20 construction to ChaCha20
	  rather than to Salsa20.  XChaCha20 extends ChaCha20's nonce length
	  from 64 bits (or 96 bits using the RFC7539 convention) to 192 bits,
	  while provably retaining ChaCha20's security.  See also:
	  <https://cr.yp.to/snuffle/xsalsa-20081128.pdf>

config CRYPTO_CHACHA20_X86_64
config CRYPTO_CHACHA20_X86_64
	tristate "ChaCha20 cipher algorithm (x86_64/SSSE3/AVX2)"
	tristate "ChaCha20 cipher algorithm (x86_64/SSSE3/AVX2)"
	depends on X86 && 64BIT
	depends on X86 && 64BIT
+84 −36
Original line number Original line Diff line number Diff line
/*
/*
 * ChaCha20 256-bit cipher algorithm, RFC7539
 * ChaCha20 (RFC7539) and XChaCha20 stream cipher algorithms
 *
 *
 * Copyright (C) 2015 Martin Willi
 * Copyright (C) 2015 Martin Willi
 * Copyright (C) 2018 Google LLC
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * it under the terms of the GNU General Public License as published by
@@ -36,6 +37,31 @@ static void chacha20_docrypt(u32 *state, u8 *dst, const u8 *src,
	}
	}
}
}


static int chacha20_stream_xor(struct skcipher_request *req,
			       struct chacha20_ctx *ctx, u8 *iv)
{
	struct skcipher_walk walk;
	u32 state[16];
	int err;

	err = skcipher_walk_virt(&walk, req, false);

	crypto_chacha20_init(state, ctx, iv);

	while (walk.nbytes > 0) {
		unsigned int nbytes = walk.nbytes;

		if (nbytes < walk.total)
			nbytes = round_down(nbytes, walk.stride);

		chacha20_docrypt(state, walk.dst.virt.addr, walk.src.virt.addr,
				 nbytes);
		err = skcipher_walk_done(&walk, walk.nbytes - nbytes);
	}

	return err;
}

void crypto_chacha20_init(u32 *state, struct chacha20_ctx *ctx, u8 *iv)
void crypto_chacha20_init(u32 *state, struct chacha20_ctx *ctx, u8 *iv)
{
{
	state[0]  = 0x61707865; /* "expa" */
	state[0]  = 0x61707865; /* "expa" */
@@ -77,30 +103,34 @@ int crypto_chacha20_crypt(struct skcipher_request *req)
{
{
	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
	struct chacha20_ctx *ctx = crypto_skcipher_ctx(tfm);
	struct chacha20_ctx *ctx = crypto_skcipher_ctx(tfm);
	struct skcipher_walk walk;
	u32 state[16];
	int err;

	err = skcipher_walk_virt(&walk, req, false);


	crypto_chacha20_init(state, ctx, walk.iv);
	return chacha20_stream_xor(req, ctx, req->iv);
}
EXPORT_SYMBOL_GPL(crypto_chacha20_crypt);


	while (walk.nbytes > 0) {
int crypto_xchacha20_crypt(struct skcipher_request *req)
		unsigned int nbytes = walk.nbytes;
{
	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
	struct chacha20_ctx *ctx = crypto_skcipher_ctx(tfm);
	struct chacha20_ctx subctx;
	u32 state[16];
	u8 real_iv[16];


		if (nbytes < walk.total)
	/* Compute the subkey given the original key and first 128 nonce bits */
			nbytes = round_down(nbytes, walk.stride);
	crypto_chacha20_init(state, ctx, req->iv);
	hchacha20_block(state, subctx.key);


		chacha20_docrypt(state, walk.dst.virt.addr, walk.src.virt.addr,
	/* Build the real IV */
				 nbytes);
	memcpy(&real_iv[0], req->iv + 24, 8); /* stream position */
		err = skcipher_walk_done(&walk, walk.nbytes - nbytes);
	memcpy(&real_iv[8], req->iv + 16, 8); /* remaining 64 nonce bits */
	}


	return err;
	/* Generate the stream and XOR it with the data */
	return chacha20_stream_xor(req, &subctx, real_iv);
}
}
EXPORT_SYMBOL_GPL(crypto_chacha20_crypt);
EXPORT_SYMBOL_GPL(crypto_xchacha20_crypt);


static struct skcipher_alg alg = {
static struct skcipher_alg algs[] = {
	{
		.base.cra_name		= "chacha20",
		.base.cra_name		= "chacha20",
		.base.cra_driver_name	= "chacha20-generic",
		.base.cra_driver_name	= "chacha20-generic",
		.base.cra_priority	= 100,
		.base.cra_priority	= 100,
@@ -115,16 +145,32 @@ static struct skcipher_alg alg = {
		.setkey			= crypto_chacha20_setkey,
		.setkey			= crypto_chacha20_setkey,
		.encrypt		= crypto_chacha20_crypt,
		.encrypt		= crypto_chacha20_crypt,
		.decrypt		= crypto_chacha20_crypt,
		.decrypt		= crypto_chacha20_crypt,
	}, {
		.base.cra_name		= "xchacha20",
		.base.cra_driver_name	= "xchacha20-generic",
		.base.cra_priority	= 100,
		.base.cra_blocksize	= 1,
		.base.cra_ctxsize	= sizeof(struct chacha20_ctx),
		.base.cra_module	= THIS_MODULE,

		.min_keysize		= CHACHA20_KEY_SIZE,
		.max_keysize		= CHACHA20_KEY_SIZE,
		.ivsize			= XCHACHA20_IV_SIZE,
		.chunksize		= CHACHA20_BLOCK_SIZE,
		.setkey			= crypto_chacha20_setkey,
		.encrypt		= crypto_xchacha20_crypt,
		.decrypt		= crypto_xchacha20_crypt,
	}
};
};


static int __init chacha20_generic_mod_init(void)
static int __init chacha20_generic_mod_init(void)
{
{
	return crypto_register_skcipher(&alg);
	return crypto_register_skciphers(algs, ARRAY_SIZE(algs));
}
}


static void __exit chacha20_generic_mod_fini(void)
static void __exit chacha20_generic_mod_fini(void)
{
{
	crypto_unregister_skcipher(&alg);
	crypto_unregister_skciphers(algs, ARRAY_SIZE(algs));
}
}


module_init(chacha20_generic_mod_init);
module_init(chacha20_generic_mod_init);
@@ -132,6 +178,8 @@ module_exit(chacha20_generic_mod_fini);


MODULE_LICENSE("GPL");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
MODULE_DESCRIPTION("chacha20 cipher algorithm");
MODULE_DESCRIPTION("ChaCha20 and XChaCha20 stream ciphers (generic)");
MODULE_ALIAS_CRYPTO("chacha20");
MODULE_ALIAS_CRYPTO("chacha20");
MODULE_ALIAS_CRYPTO("chacha20-generic");
MODULE_ALIAS_CRYPTO("chacha20-generic");
MODULE_ALIAS_CRYPTO("xchacha20");
MODULE_ALIAS_CRYPTO("xchacha20-generic");
+6 −0
Original line number Original line Diff line number Diff line
@@ -3576,6 +3576,12 @@ static const struct alg_test_desc alg_test_descs[] = {
		.suite = {
		.suite = {
			.hash = __VECS(aes_xcbc128_tv_template)
			.hash = __VECS(aes_xcbc128_tv_template)
		}
		}
	}, {
		.alg = "xchacha20",
		.test = alg_test_skcipher,
		.suite = {
			.cipher = __VECS(xchacha20_tv_template)
		},
	}, {
	}, {
		.alg = "xts(aes)",
		.alg = "xts(aes)",
		.test = alg_test_skcipher,
		.test = alg_test_skcipher,