Skip to content

Commit

Permalink
random: use chacha20 for get_random_int/long
Browse files Browse the repository at this point in the history
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: Jason A. Donenfeld <Jason@zx2c4.com>
Suggested-by: Theodore 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: Theodore Ts'o <tytso@mit.edu>
  • Loading branch information
Jason A. Donenfeld authored and Theodore Ts'o committed Jan 27, 2017
1 parent 5d0e5ea commit f5b9846
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 43 deletions.
84 changes: 43 additions & 41 deletions drivers/char/random.c
Original file line number Diff line number Diff line change
Expand Up @@ -2016,63 +2016,65 @@ struct ctl_table random_table[] = {
};
#endif /* CONFIG_SYSCTL */

static u32 random_int_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned;

int random_int_secret_init(void)
{
get_random_bytes(random_int_secret, sizeof(random_int_secret));
return 0;
}

static DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash)
__aligned(sizeof(unsigned long));
struct batched_entropy {
union {
unsigned long entropy_long[CHACHA20_BLOCK_SIZE / sizeof(unsigned long)];
unsigned int entropy_int[CHACHA20_BLOCK_SIZE / sizeof(unsigned int)];
};
unsigned int position;
};

/*
* Get a random word for internal kernel use only. Similar to urandom but
* with the goal of minimal entropy pool depletion. As a result, the random
* value is not cryptographically secure but for several uses the cost of
* depleting entropy is too high
* Get a random word for internal kernel use only. The quality of the random
* number is either as good as RDRAND or as good as /dev/urandom, with the
* goal of being quite fast and not depleting entropy.
*/
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 int ret;
unsigned long ret;
struct batched_entropy *batch;

if (arch_get_random_int(&ret))
if (arch_get_random_long(&ret))
return ret;

hash = get_cpu_var(get_random_int_hash);

hash[0] += current->pid + jiffies + random_get_entropy();
md5_transform(hash, random_int_secret);
ret = hash[0];
put_cpu_var(get_random_int_hash);

batch = &get_cpu_var(batched_entropy_long);
if (batch->position % ARRAY_SIZE(batch->entropy_long) == 0) {
extract_crng((u8 *)batch->entropy_long);
batch->position = 0;
}
ret = batch->entropy_long[batch->position++];
put_cpu_var(batched_entropy_long);
return ret;
}
EXPORT_SYMBOL(get_random_int);
EXPORT_SYMBOL(get_random_long);

/*
* Same as get_random_int(), but returns unsigned long.
*/
unsigned long get_random_long(void)
#if BITS_PER_LONG == 32
unsigned int get_random_int(void)
{
__u32 *hash;
unsigned long ret;
return get_random_long();
}
#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;

hash = get_cpu_var(get_random_int_hash);

hash[0] += current->pid + jiffies + random_get_entropy();
md5_transform(hash, random_int_secret);
ret = *(unsigned long *)hash;
put_cpu_var(get_random_int_hash);

batch = &get_cpu_var(batched_entropy_int);
if (batch->position % ARRAY_SIZE(batch->entropy_int) == 0) {
extract_crng((u8 *)batch->entropy_int);
batch->position = 0;
}
ret = batch->entropy_int[batch->position++];
put_cpu_var(batched_entropy_int);
return ret;
}
EXPORT_SYMBOL(get_random_long);
#endif
EXPORT_SYMBOL(get_random_int);

/**
* randomize_page - Generate a random, page aligned address
Expand Down
1 change: 0 additions & 1 deletion include/linux/random.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 void del_random_ready_callback(struct random_ready_callback *rdy);
extern void get_random_bytes_arch(void *buf, int nbytes);
extern int random_int_secret_init(void);

#ifndef MODULE
extern const struct file_operations random_fops, urandom_fops;
Expand Down
1 change: 0 additions & 1 deletion init/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -879,7 +879,6 @@ static void __init do_basic_setup(void)
do_ctors();
usermodehelper_enable();
do_initcalls();
random_int_secret_init();
}

static void __init do_pre_smp_initcalls(void)
Expand Down

0 comments on commit f5b9846

Please sign in to comment.