[RFC PATCH v1 50/50] random: add get_random_nonce()

From: George Spelvin
Date: Sat Mar 28 2020 - 12:45:10 EST


This is like get_random_bytes(), but uses the same batched entropy
buffer as get_random_u32().

Many kernel users of get_random_bytes could be switched to this.
E.g. eth_random_addr().

But before doing that, we should decide if it wouldn't be better
to rename this function to get_random_bytes, and let the smaller
number of sites which really need strongly secure random numbers
use a different name like "get_secret_bytes()".

Code size +731 bytes (x86-64)

Signed-off-by: George Spelvin <lkml@xxxxxxx>
Cc: Theodore Ts'o <tytso@xxxxxxx>
Cc: Jason A. Donenfeld <Jason@xxxxxxxxx>
---
drivers/char/random.c | 77 ++++++++++++++++++++++++++++++++++++++++++
include/linux/random.h | 1 +
2 files changed, 78 insertions(+)

diff --git a/drivers/char/random.c b/drivers/char/random.c
index 67bdfb51eb25c..02e80000310ce 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -342,6 +342,7 @@
#include <asm/irq.h>
#include <asm/irq_regs.h>
#include <asm/io.h>
+#include <asm/unaligned.h>

#define CREATE_TRACE_POINTS
#include <trace/events/random.h>
@@ -2569,6 +2570,82 @@ u32 get_random_u32(void)
}
EXPORT_SYMBOL(get_random_u32);

+/**
+ * get_random_nonce - Generate a random string of bytes
+ * @buf: The buffer to fill
+ * @nbytes: The size of the buffer
+ *
+ * This generates cryptographically strong bytes, at less cost than
+ * get_random_bytes. In particular, anti-backtracking is not
+ * provided. It is suitable for public values (random cookies or
+ * MAC addresses which are supposed to be unguessable), and for
+ * secret values which are stored in the kernel as long as they
+ * are valuable, such as SipHash keys or stack canaries.
+ *
+ * In general, if the calling code does not use memzero_explicit(), it
+ * is safe to use this function rather than get_random_bytes.
+ *
+ * TODO: Perhaps get_random_bytes should be renamed to get_secret_bytes
+ * and this function should be renamed to get_random_bytes?
+ */
+void get_random_nonce(void *buf, size_t nbytes)
+{
+ unsigned long ret;
+ size_t pos;
+ unsigned long flags;
+ struct batched_entropy *batch;
+ static void *previous;
+
+#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+ while (nbytes >= sizeof(ret)) {
+ if (!arch_get_random_long(buf))
+ goto software;
+ buf += sizeof(ret);
+ nbytes -= sizeof(ret);
+ }
+ if (nbytes) {
+ if (!arch_get_random_long(&ret))
+ goto software;
+ memcpy(buf, &ret, nbytes);
+ }
+ return;
+software:
+#else
+ while (arch_get_random_long(&ret)) {
+ if (nbytes > sizeof(ret)) {
+ put_unaligned(ret, (unsigned long *)buf);
+ buf += sizeof(ret);
+ nbytes -= sizeof(ret);
+ } else {
+ memcpy(buf, &ret, nbytes);
+ return;
+ }
+ }
+#endif
+
+ warn_unseeded_randomness(&previous);
+
+ local_irq_save(flags);
+ batch = this_cpu_ptr(&batched_entropy);
+ pos = batch->position + 1;
+ if (unlikely(crng_init != batch->crng_init)) {
+ batch->crng_init = crng_init;
+ pos = CHACHA_BLOCK_SIZE;
+ }
+
+ while (nbytes > CHACHA_BLOCK_SIZE - pos) {
+ memcpy(buf, batch->entropy8 + pos, CHACHA_BLOCK_SIZE - pos);
+ buf += CHACHA_BLOCK_SIZE - pos;
+ nbytes -= CHACHA_BLOCK_SIZE - pos;
+ extract_crng(batch->entropy32);
+ pos = 0;
+ }
+ batch->position = pos + nbytes - 1;
+ memcpy(buf, batch->entropy8 + pos, nbytes);
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL(get_random_nonce);
+
/**
* __get_random_max32 - Generate a uniform random number 0 <= x < range < 2^32
* @range: Modulus for random number; must not be zero!
diff --git a/include/linux/random.h b/include/linux/random.h
index 97d3469f3aad8..de9eae20b69af 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -50,6 +50,7 @@ extern const struct file_operations random_fops, urandom_fops;

u32 get_random_u32(void);
u64 get_random_u64(void);
+void get_random_nonce(void *buf, size_t nbytes);
static inline unsigned int get_random_int(void)
{
return get_random_u32();
--
2.26.0