Commit af729160 authored by Jason A. Donenfeld's avatar Jason A. Donenfeld Committed by Greg Kroah-Hartman

random: use chacha20 for get_random_int/long

commit f5b98461 upstream.

Now that our crng uses chacha20, we can rely on its speedy
characteristics for replacing MD5, while simultaneously achieving a
higher security guarantee. Before the idea was to use these functions if
you wanted random integers that aren't stupidly insecure but aren't
necessarily secure either, a vague gray zone, that hopefully was "good
enough" for its users. With chacha20, we can strengthen this claim,
since either we're using an rdrand-like instruction, or we're using the
same crng as /dev/urandom. And it's faster than what was before.

We could have chosen to replace this with a SipHash-derived function,
which might be slightly faster, but at the cost of having yet another
RNG construction in the kernel. By moving to chacha20, we have a single
RNG to analyze and verify, and we also already get good performance
improvements on all platforms.

Implementation-wise, rather than use a generic buffer for both
get_random_int/long and memcpy based on the size needs, we use a
specific buffer for 32-bit reads and for 64-bit reads. This way, we're
guaranteed to always have aligned accesses on all platforms. While
slightly more verbose in C, the assembly this generates is a lot
simpler than otherwise.

Finally, on 32-bit platforms where longs and ints are the same size,
we simply alias get_random_int to get_random_long.
Signed-off-by: default avatarJason A. Donenfeld <Jason@zx2c4.com>
Suggested-by: default avatarTheodore Ts'o <tytso@mit.edu>
Cc: Theodore Ts'o <tytso@mit.edu>
Cc: Hannes Frederic Sowa <hannes@stressinduktion.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent d57c764a
...@@ -2042,63 +2042,65 @@ struct ctl_table random_table[] = { ...@@ -2042,63 +2042,65 @@ struct ctl_table random_table[] = {
}; };
#endif /* CONFIG_SYSCTL */ #endif /* CONFIG_SYSCTL */
static u32 random_int_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned; struct batched_entropy {
union {
int random_int_secret_init(void) unsigned long entropy_long[CHACHA20_BLOCK_SIZE / sizeof(unsigned long)];
{ unsigned int entropy_int[CHACHA20_BLOCK_SIZE / sizeof(unsigned int)];
get_random_bytes(random_int_secret, sizeof(random_int_secret)); };
return 0; unsigned int position;
} };
static DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash)
__aligned(sizeof(unsigned long));
/* /*
* Get a random word for internal kernel use only. Similar to urandom but * Get a random word for internal kernel use only. The quality of the random
* with the goal of minimal entropy pool depletion. As a result, the random * number is either as good as RDRAND or as good as /dev/urandom, with the
* value is not cryptographically secure but for several uses the cost of * goal of being quite fast and not depleting entropy.
* depleting entropy is too high
*/ */
unsigned int get_random_int(void) static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_long);
unsigned long get_random_long(void)
{ {
__u32 *hash; unsigned long ret;
unsigned int ret; struct batched_entropy *batch;
if (arch_get_random_int(&ret)) if (arch_get_random_long(&ret))
return ret; return ret;
hash = get_cpu_var(get_random_int_hash); batch = &get_cpu_var(batched_entropy_long);
if (batch->position % ARRAY_SIZE(batch->entropy_long) == 0) {
hash[0] += current->pid + jiffies + random_get_entropy(); extract_crng((u8 *)batch->entropy_long);
md5_transform(hash, random_int_secret); batch->position = 0;
ret = hash[0]; }
put_cpu_var(get_random_int_hash); ret = batch->entropy_long[batch->position++];
put_cpu_var(batched_entropy_long);
return ret; return ret;
} }
EXPORT_SYMBOL(get_random_int); EXPORT_SYMBOL(get_random_long);
/* #if BITS_PER_LONG == 32
* Same as get_random_int(), but returns unsigned long. unsigned int get_random_int(void)
*/
unsigned long get_random_long(void)
{ {
__u32 *hash; return get_random_long();
unsigned long ret; }
#else
static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_int);
unsigned int get_random_int(void)
{
unsigned int ret;
struct batched_entropy *batch;
if (arch_get_random_long(&ret)) if (arch_get_random_int(&ret))
return ret; return ret;
hash = get_cpu_var(get_random_int_hash); batch = &get_cpu_var(batched_entropy_int);
if (batch->position % ARRAY_SIZE(batch->entropy_int) == 0) {
hash[0] += current->pid + jiffies + random_get_entropy(); extract_crng((u8 *)batch->entropy_int);
md5_transform(hash, random_int_secret); batch->position = 0;
ret = *(unsigned long *)hash; }
put_cpu_var(get_random_int_hash); ret = batch->entropy_int[batch->position++];
put_cpu_var(batched_entropy_int);
return ret; return ret;
} }
EXPORT_SYMBOL(get_random_long); #endif
EXPORT_SYMBOL(get_random_int);
/** /**
* randomize_page - Generate a random, page aligned address * randomize_page - Generate a random, page aligned address
......
...@@ -37,7 +37,6 @@ extern void get_random_bytes(void *buf, int nbytes); ...@@ -37,7 +37,6 @@ extern void get_random_bytes(void *buf, int nbytes);
extern int add_random_ready_callback(struct random_ready_callback *rdy); extern int add_random_ready_callback(struct random_ready_callback *rdy);
extern void del_random_ready_callback(struct random_ready_callback *rdy); extern void del_random_ready_callback(struct random_ready_callback *rdy);
extern void get_random_bytes_arch(void *buf, int nbytes); extern void get_random_bytes_arch(void *buf, int nbytes);
extern int random_int_secret_init(void);
#ifndef MODULE #ifndef MODULE
extern const struct file_operations random_fops, urandom_fops; extern const struct file_operations random_fops, urandom_fops;
......
...@@ -879,7 +879,6 @@ static void __init do_basic_setup(void) ...@@ -879,7 +879,6 @@ static void __init do_basic_setup(void)
do_ctors(); do_ctors();
usermodehelper_enable(); usermodehelper_enable();
do_initcalls(); do_initcalls();
random_int_secret_init();
} }
static void __init do_pre_smp_initcalls(void) static void __init do_pre_smp_initcalls(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment