random: use chacha20 for get_random_int/long
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>
This commit is contained in:
parent
5d0e5ea343
commit
f5b98461cb
@ -2016,64 +2016,66 @@ 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
|
||||
*/
|
||||
unsigned int get_random_int(void)
|
||||
{
|
||||
__u32 *hash;
|
||||
unsigned int 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 = hash[0];
|
||||
put_cpu_var(get_random_int_hash);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(get_random_int);
|
||||
|
||||
/*
|
||||
* Same as get_random_int(), but returns unsigned long.
|
||||
* 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.
|
||||
*/
|
||||
static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_long);
|
||||
unsigned long get_random_long(void)
|
||||
{
|
||||
__u32 *hash;
|
||||
unsigned long ret;
|
||||
struct batched_entropy *batch;
|
||||
|
||||
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 = *(unsigned long *)hash;
|
||||
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_long);
|
||||
|
||||
#if BITS_PER_LONG == 32
|
||||
unsigned int get_random_int(void)
|
||||
{
|
||||
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_int(&ret))
|
||||
return ret;
|
||||
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
EXPORT_SYMBOL(get_random_int);
|
||||
|
||||
/**
|
||||
* randomize_page - Generate a random, page aligned address
|
||||
* @start: The smallest acceptable address the caller will take.
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user