random: use chacha20 for get_random_int/long
authorJason A. Donenfeld <Jason@zx2c4.com>
Fri, 6 Jan 2017 18:32:01 +0000 (19:32 +0100)
committerTheodore Ts'o <tytso@mit.edu>
Fri, 27 Jan 2017 19:25:06 +0000 (14:25 -0500)
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>
drivers/char/random.c
include/linux/random.h
init/main.c

index 9d147d456598e257f27a4c1de4af7e4f6397fabd..b800e5479b7dc8065ce0e12a6a521ba04f188667 100644 (file)
@@ -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
index 7bd2403e4fef1ad7fb0a5f03b4e104e96234d26b..16ab429735a783f0d68943437d19162c62032b07 100644 (file)
@@ -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;
index b0c9d6facef9a5aced55d1443b40029a660011e8..09beb7fc6e8c7b0d413916c8d585e3e5aa4aaf5f 100644 (file)
@@ -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)