Skip to content
Permalink
Browse files
Linux Random Number Generator
In an effort to provide a flexible implementation for a random number
generator that also delivers entropy during early boot time, allows
replacement of the deterministic random number generation mechanism,
implement the various components in separate code for easier
maintenance, and provide compliance to SP800-90[A|B|C], introduce
the Linux Random Number Generator (LRNG) framework.

The general design is as follows. Additional implementation details
are given in [1]. The LRNG consists of the following components:

1. The LRNG implements a DRNG. The DRNG always generates the
requested amount of output. When using the SP800-90A terminology
it operates without prediction resistance. The secondary DRNG
maintains a counter of how many bytes were generated since last
re-seed and a timer of the elapsed time since last re-seed. If either
the counter or the timer reaches a threshold, the secondary DRNG is
seeded from the entropy pool.

In case the Linux kernel detects a NUMA system, one secondary DRNG
instance per NUMA node is maintained.

2. The DRNG is seeded by concatenating the data from the
following sources:

(a) the output of the entropy pool,

(b) the Jitter RNG if available and enabled, and

(c) the CPU-based noise source such as Intel RDRAND if available and
enabled.

The entropy estimate of the data of all noise sources are added to
form the entropy estimate of the data used to seed the DRNG with.
The LRNG ensures, however, that the DRNG after seeding is at
maximum the security strength of the DRNG.

The LRNG is designed such that none of these noise sources can dominate
the other noise sources to provide seed data to the DRNG during due to
the following:

(a) During boot time, the amount of received interrupts are the trigger
points to (re)seed the DRNG.

(b) At runtime, the available entropy from the slow noise source is
concatenated with a pre-defined amount of data from the fast noise
sources. In addition, each DRNG reseed operation triggers external
noise source providers to deliver one block of data.

3. The entropy pool accumulates entropy obtained from certain events,
which will henceforth be collectively called "slow noise sources".
The entropy pool collects noise data from slow noise sources. Any data
received by the LRNG from the slow noise sources is inserted into the
entropy pool using an LFSR with a primitive and irreducible polynomial.
The following sources of entropy are used:

 (a) When an interrupt occurs, the high-resolution time stamp is mixed
into the LFSR. This time stamp is credited with heuristically implied
entropy.

 (b) HID event data like the key stroke or the mouse coordinates are
mixed into the LFSR. This data is not credited with entropy by the LRNG.

 (c) Device drivers may provide data that is mixed into the LFSR. This
data is not credited with entropy by the LRNG.

 (d) After the entropy pool is ``read'' by the DRNG, the data
used to seed the DRNG is mixed back into the entropy pool to
stir the pool. This data is not credited with entropy by the LRNG.

Any data provided from user space by either writing to /dev/random,
/dev/urandom or the IOCTL of RNDADDENTROPY on both device files
are always injected into the entropy pool.

In addition, when a hardware random number generator covered by the
Linux kernel HW generator framework wants to deliver random numbers,
it is injected into the entropy pool as well. HW generator noise source
is handled separately from the other noise source due to the fact that
the HW generator framework may decide by itself when to deliver data
whereas the other noise sources always requested for data driven by the
LRNG operation. Similarly any user space provided data is inserted into
the entropy pool.

When the DRNG requires data from the entropy pool, the entire
entropy pool is processed with an SP800-90A section 10.3.1 compliant
hash_df function to generate random numbers.

To speed up the interrupt handling code of the LRNG, the time stamp
collected for an interrupt event is truncated to the 8 least
significant bits. 64 truncated time stamps are concatenated and then
jointly inserted into the LFSR. During boot time, until the fully seeded
stage is reached, each time stamp with its 32 least significant bits is
inserted into the LFSR at the time of arrival.

The LRNG allows the DRNG mechanism to be changed at runtime. Per default,
a ChaCha20-based DRNG is used. The ChaCha20-DRNG implemented for the
LRNG is also provided as a stand-alone user space deterministic random
number generator. The LRNG also offers an SP800-90A DRBG based on the
Linux kernel crypto API DRBG implementation.

The processing of entropic data from the noise source before injecting
them into the DRNG is performed with the following mathematical
operations:

1. LFSR: The 8 least significant bits of the time stamp data received
from the interrupts are processed with an LFSR. That LFSR is implemented
identically to the LSFR used in the existing /dev/random implementation
except that it is capable of processing an entire word and that a
different polynomial is used. The reason for the different polynomial
is performance in a performance sensitive code section, the interrupt
handler. The chosen polynomials have 4 taps. Also, this LFSR-approach
is used in the OpenBSD /dev/random equivalent.

2. Concatenation: The temporary seed buffer used to seed the DRNG is
a concatenation of parts of the entropy pool data, and the CPU noise
source output.

The DRNG always tries to seed itself with 256 bits of entropy, except
during boot. In any case, if the noise sources cannot deliver that
amount, the available entropy is used and the DRNG keeps track on how
much entropy it was seeded with. The entropy implied by the LRNG
available in the entropy pool may be too conservative. To ensure
that during boot time all available entropy from the entropy pool is
transferred to the DRNG, the hash_df function always generates 256
data bits during boot to seed the DRNG. During boot, the DRNG is
seeded as follows:

1. The DRNG is reseeded from the entropy pool and potentially the fast
noise sources if the entropy pool has collected at least 32 bits of
entropy from the interrupt noise source. The goal of this step is to
ensure that the DRNG receives some initial entropy as early as
possible. In addition it receives the entropy available from
the fast noise sources.

2. The DRNG is reseeded from the entropy pool and potentially the fast
noise sources if all noise sources collectively can provide at least
128 bits of entropy.

3. The DRNG is reseeded from the entropy pool and potentially the fast
noise sources if all noise sources collectivel can provide at least 256
bits.

At the time of the reseeding steps, the DRNG requests as much entropy as
is available in order to skip certain steps and reach the seeding level
of 256 bits. This may imply that one or more of the aforementioned steps
are skipped.

In all listed steps, the DRNG is (re)seeded with a number of random
bytes from the entropy pool that is at most the amount of entropy
present in the entropy pool. This means that when the entropy pool
contains 128 or 256 bits of entropy, the DRNG is seeded with that
amount of entropy as well.

Before the DRNG is seeded with 256 bits of entropy in step 3,
requests of random data from /dev/random are not processed.

The hash_df operation providing random data from the entropy pool will
always require that all entropy sources collectively can deliver at
least 129 entropy bits as configured with (128 bits of entropy for
seeding plus one bit of entropy that is lost with the post
processing as defined in SP800-90B).

The DRNG operates as deterministic random number generator with the
following properties:

* The maximum number of random bytes that can be generated with one
DRNG generate operation is limited to 4096 bytes. When longer random
numbers are requested, multiple DRNG generate operations are performed.
The ChaCha20 DRNG as well as the SP800-90A DRBGs implement an update of
their state after completing a generate request for backtracking
resistance.

* The secondary DRNG is reseeded with whatever entropy is available –
in the worst case where no additional entropy can be provided by the
noise sources, the DRNG is not re-seeded and continues its operation
to try to reseed again after again the expiry of one of these thresholds:

 - If the last reseeding of the secondary DRNG is more than 600 seconds
   ago, or

 - 2^20 DRNG generate operations are performed, whatever comes first, or

 - the secondary DRNG is forced to reseed before the next generation of
   random numbers if data has been injected into the LRNG by writing data
   into /dev/random or /dev/urandom.

The chosen values prevent high-volume requests from user space to cause
frequent reseeding operations which drag down the performance of the
DRNG.

With the automatic reseeding after 600 seconds, the LRNG is triggered
to reseed itself before the first request after a suspend that put the
hardware to sleep for longer than 600 seconds.

The LRNG uses the following runtime memory using the currently
smallest configuration:

* 576 bytes (512 bytes for the entropy pool and 64 for the entropy pool
  meta data) for the entropy pool management

* 64 bytes per CPU for the time stamp array

To support smaller devices including IoT environments, this patch
allows reducing the runtime memory footprint of the LRNG at compile
time by selecting smaller entropy pool sizes.

The entropy pool has support for sizes of 256, 128 and 64 bytes supported
by primitive and irreducible polynomials.

The time stamp array is reduced to one atomic_t variable per CPU, i.e.
4 bytes when CONFIG_BASE_SMALL is selected during kernel
configuration. This implies that after the receipt of 4 interrupts on
one CPU, the data is injected into the LFSR. Depending on the behavior
of the CPU caches, this may imply that the average interrupt handler
execution time increases a bit, since instead of injecting 8 atomic_t
values at one given time into the LFSR, only one is processed which
may incur cache misses.

When selecting the compilation of a kernel for a small environment,
prevent the allocation of a buffer up to 4096 bytes to serve user space
requests. In this case, the stack variable of 64 bytes is used to serve
all user space requests.

The LRNG has the following properties:

* internal noise source: interrupts timing with fast boot time seeding

* high performance of interrupt handling code: The LRNG impact on the
interrupt handling has been reduced to a minimum. On one example
system, the LRNG interrupt handling code executes within an average
of 65 cycles whereas the existing /dev/random on the same device
takes about 97 cycles when measuring the execution time of
add_interrupt_randomness().

* lockless LFSR to collect raw entropy supporing concurrency-free
  use of massive parallel systems

* use of standalone ChaCha20 based RNG with the option to use a
  different DRNG selectable at compile time

* "atomic" seeding of secondary DRBG to ensure full entropy transport

* instantiate one DRNG per NUMA node

* support for runtime switchable output DRNGs

* use of only well-defined entropy-preserving operations to collect,
compress and forward entropy: concatenation, LFSR, SP800-90A hash_df
function

* compile-time selectable entropy pool size: the choice also
uses the applicable LFSR polynomial to maintain the entropy pool
size

* support of small systems by allowing the reduction of the
runtime memory needs

Further details including the rationale for the design choices and
properties of the LRNG together with testing is provided at [1].
In addition, the documentation explains the conducted regression
tests to verify that the LRNG is API and ABI compatible with the
existing /dev/random implementation.

[1] https://www.chronox.de/lrng.html

CC: "Eric W. Biederman" <ebiederm@xmission.com>
CC: "Alexander E. Patrakov" <patrakov@gmail.com>
CC: "Ahmed S. Darwish" <darwish.07@gmail.com>
CC: "Theodore Y. Ts'o" <tytso@mit.edu>
CC: Willy Tarreau <w@1wt.eu>
CC: Matthew Garrett <mjg59@srcf.ucam.org>
CC: Vito Caputo <vcaputo@pengaru.com>
CC: Andreas Dilger <adilger.kernel@dilger.ca>
CC: Jan Kara <jack@suse.cz>
CC: Ray Strode <rstrode@redhat.com>
CC: William Jon McCann <mccann@jhu.edu>
CC: zhangjs <zachary@baishancloud.com>
CC: Andy Lutomirski <luto@kernel.org>
CC: Florian Weimer <fweimer@redhat.com>
CC: Lennart Poettering <mzxreary@0pointer.de>
CC: Nicolai Stange <nstange@suse.de>
Mathematical aspects Reviewed-by: "Peter, Matthias" <matthias.peter@bsi.bund.de>
Reviewed-by: Marcelo Henrique Cerri <marcelo.cerri@canonical.com>
Reviewed-by: Roman Drahtmueller <draht@schaltsekun.de>
Tested-by: Roman Drahtmüller <draht@schaltsekun.de>
Tested-by: Marcelo Henrique Cerri <marcelo.cerri@canonical.com>
Tested-by: Neil Horman <nhorman@redhat.com>
Signed-off-by: Stephan Mueller <smueller@chronox.de>
  • Loading branch information
smuellerDD authored and intel-lab-lkp committed Aug 20, 2020
1 parent d162219 commit 866aae82856f1fba6af5c4b19a3905800cab4563
Show file tree
Hide file tree
Showing 17 changed files with 3,078 additions and 1 deletion.
@@ -10084,6 +10084,13 @@ F: Documentation/litmus-tests/
F: Documentation/memory-barriers.txt
F: tools/memory-model/

LINUX RANDOM NUMBER GENERATOR (LRNG) DRIVER
M: Stephan Mueller <smueller@chronox.de>
S: Maintained
W: https://www.chronox.de/lrng.html
F: drivers/char/lrng/*
F: include/linux/lrng.h

LIS3LV02D ACCELEROMETER DRIVER
M: Eric Piel <eric.piel@tremplin-utc.net>
S: Maintained
@@ -470,6 +470,8 @@ config ADI
and SSM (Silicon Secured Memory). Intended consumers of this
driver include crash and makedumpfile.

source "drivers/char/lrng/Kconfig"

endmenu

config RANDOM_TRUST_CPU
@@ -3,7 +3,14 @@
# Makefile for the kernel character device drivers.
#

obj-y += mem.o random.o
obj-y += mem.o

ifeq ($(CONFIG_LRNG),y)
obj-y += lrng/
else
obj-y += random.o
endif

obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o
obj-y += misc.o
obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o
@@ -0,0 +1,68 @@
# SPDX-License-Identifier: GPL-2.0
#
# Linux Random Number Generator configuration
#

menuconfig LRNG
bool "Linux Random Number Generator"
select CRYPTO_LIB_SHA256 if CRYPTO
help
The Linux Random Number Generator (LRNG) is the replacement
of the existing /dev/random provided with drivers/char/random.c.
It generates entropy from different noise sources and
delivers significant entropy during boot.

if LRNG

choice
prompt "LRNG Entropy Pool Size"
default LRNG_POOL_SIZE_4096
help
Select the size of the LRNG entropy pool. The size of the
entropy pool is relevant for the amount of entropy that
the LRNG can maintain as a maximum. The larger the size
of the entropy pool is the more entropy can be maintained
but the less often older entropic values are overwritten
with new entropy.

config LRNG_POOL_SIZE_512
bool "512 bits"

config LRNG_POOL_SIZE_1024
bool "1024 bits"

config LRNG_POOL_SIZE_2048
bool "2048 bits"

config LRNG_POOL_SIZE_4096
bool "4096 bits (default)"

config LRNG_POOL_SIZE_8192
bool "8192 bits"

config LRNG_POOL_SIZE_16384
bool "16384 bits"

config LRNG_POOL_SIZE_32768
bool "32768 bits"

config LRNG_POOL_SIZE_65536
bool "65536 bits"

config LRNG_POOL_SIZE_131072
bool "131072 bits"
endchoice

config LRNG_POOL_SIZE
int
default 0 if LRNG_POOL_SIZE_512
default 1 if LRNG_POOL_SIZE_1024
default 2 if LRNG_POOL_SIZE_2048
default 3 if LRNG_POOL_SIZE_4096
default 4 if LRNG_POOL_SIZE_8192
default 5 if LRNG_POOL_SIZE_16384
default 6 if LRNG_POOL_SIZE_32768
default 7 if LRNG_POOL_SIZE_65536
default 8 if LRNG_POOL_SIZE_131072

endif # LRNG
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the Linux Random Number Generator.
#

obj-y += lrng_pool.o lrng_aux.o \
lrng_sw_noise.o lrng_archrandom.o \
lrng_drng.o lrng_chacha20.o \
lrng_interfaces.o \
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
/*
* LRNG Fast Noise Source: CPU-based noise source
*
* Copyright (C) 2016 - 2020, Stephan Mueller <smueller@chronox.de>
*/

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/random.h>

#include "lrng_internal.h"

/*
* Estimated entropy of data is a 32th of LRNG_DRNG_SECURITY_STRENGTH_BITS.
* As we have no ability to review the implementation of those noise sources,
* it is prudent to have a conservative estimate here.
*/
#define LRNG_ARCHRANDOM_DEFAULT_STRENGTH (LRNG_DRNG_SECURITY_STRENGTH_BITS>>5)
#define LRNG_ARCHRANDOM_TRUST_CPU_STRENGTH LRNG_DRNG_SECURITY_STRENGTH_BITS
#ifdef CONFIG_RANDOM_TRUST_CPU
static u32 archrandom = LRNG_ARCHRANDOM_TRUST_CPU_STRENGTH;
#else
static u32 archrandom = LRNG_ARCHRANDOM_DEFAULT_STRENGTH;
#endif
module_param(archrandom, uint, 0644);
MODULE_PARM_DESC(archrandom, "Entropy in bits of 256 data bits from CPU noise source (e.g. RDRAND)");

static int __init lrng_parse_trust_cpu(char *arg)
{
int ret;
bool trust_cpu = false;

ret = kstrtobool(arg, &trust_cpu);
if (ret)
return ret;

if (trust_cpu)
archrandom = LRNG_ARCHRANDOM_TRUST_CPU_STRENGTH;
else
archrandom = LRNG_ARCHRANDOM_DEFAULT_STRENGTH;

return 0;
}
early_param("random.trust_cpu", lrng_parse_trust_cpu);

/**
* lrng_get_arch() - Get CPU noise source entropy
*
* @outbuf: buffer to store entropy of size LRNG_DRNG_SECURITY_STRENGTH_BYTES
*
* Return:
* * > 0 on success where value provides the added entropy in bits
* * 0 if no fast source was available
*/
u32 lrng_get_arch(u8 *outbuf)
{
u32 i, ent_bits = archrandom;

/* operate on full blocks */
BUILD_BUG_ON(LRNG_DRNG_SECURITY_STRENGTH_BYTES % sizeof(unsigned long));
/* ensure we have aligned buffers */
BUILD_BUG_ON(LRNG_KCAPI_ALIGN % sizeof(unsigned long));

if (!ent_bits)
return 0;

for (i = 0; i < LRNG_DRNG_SECURITY_STRENGTH_BYTES;
i += sizeof(unsigned long)) {
if (!arch_get_random_seed_long((unsigned long *)(outbuf + i)) &&
!arch_get_random_long((unsigned long *)(outbuf + i))) {
archrandom = 0;
return 0;
}
}

/* Obtain entropy statement -- cap entropy to buffer size in bits */
ent_bits = min_t(u32, ent_bits, LRNG_DRNG_SECURITY_STRENGTH_BITS);
pr_debug("obtained %u bits of entropy from CPU RNG noise source\n",
ent_bits);
return ent_bits;
}

u32 lrng_slow_noise_req_entropy(u32 required_entropy_bits)
{
u32 arch_ent_bits = min_t(u32, archrandom,
LRNG_DRNG_SECURITY_STRENGTH_BITS);
u32 fast_noise_entropy = arch_ent_bits + lrng_jent_entropylevel();

if (fast_noise_entropy > required_entropy_bits)
return 0;
return (required_entropy_bits - fast_noise_entropy);
}
@@ -0,0 +1,136 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
/*
* LRNG auxiliary interfaces
*
* Copyright (C) 2019 Stephan Mueller <smueller@chronox.de>
* Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All
* Rights Reserved.
* Copyright (C) 2016 Jason Cooper <jason@lakedaemon.net>
*/

#include <linux/mm.h>
#include <linux/random.h>

#include "lrng_internal.h"

struct batched_entropy {
union {
u64 entropy_u64[LRNG_DRNG_BLOCKSIZE / sizeof(u64)];
u32 entropy_u32[LRNG_DRNG_BLOCKSIZE / sizeof(u32)];
};
unsigned int position;
spinlock_t batch_lock;
};

/*
* Get a random word for internal kernel use only. The quality of the random
* number is as good as /dev/urandom, but there is no backtrack protection,
* with the goal of being quite fast and not depleting entropy.
*/
static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_u64) = {
.batch_lock = __SPIN_LOCK_UNLOCKED(batched_entropy_u64.lock),
};

u64 get_random_u64(void)
{
u64 ret;
unsigned long flags;
struct batched_entropy *batch;

lrng_debug_report_seedlevel("get_random_u64");

batch = raw_cpu_ptr(&batched_entropy_u64);
spin_lock_irqsave(&batch->batch_lock, flags);
if (batch->position % ARRAY_SIZE(batch->entropy_u64) == 0) {
lrng_drng_get_atomic((u8 *)batch->entropy_u64,
LRNG_DRNG_BLOCKSIZE);
batch->position = 0;
}
ret = batch->entropy_u64[batch->position++];
spin_unlock_irqrestore(&batch->batch_lock, flags);
return ret;
}
EXPORT_SYMBOL(get_random_u64);

static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_u32) = {
.batch_lock = __SPIN_LOCK_UNLOCKED(batched_entropy_u32.lock),
};

u32 get_random_u32(void)
{
u32 ret;
unsigned long flags;
struct batched_entropy *batch;

lrng_debug_report_seedlevel("get_random_u32");

batch = raw_cpu_ptr(&batched_entropy_u32);
spin_lock_irqsave(&batch->batch_lock, flags);
if (batch->position % ARRAY_SIZE(batch->entropy_u32) == 0) {
lrng_drng_get_atomic((u8 *)batch->entropy_u32,
LRNG_DRNG_BLOCKSIZE);
batch->position = 0;
}
ret = batch->entropy_u32[batch->position++];
spin_unlock_irqrestore(&batch->batch_lock, flags);
return ret;
}
EXPORT_SYMBOL(get_random_u32);

/*
* It's important to invalidate all potential batched entropy that might
* be stored before the crng is initialized, which we can do lazily by
* simply resetting the counter to zero so that it's re-extracted on the
* next usage.
*/
void invalidate_batched_entropy(void)
{
int cpu;
unsigned long flags;

for_each_possible_cpu(cpu) {
struct batched_entropy *batched_entropy;

batched_entropy = per_cpu_ptr(&batched_entropy_u32, cpu);
spin_lock_irqsave(&batched_entropy->batch_lock, flags);
batched_entropy->position = 0;
spin_unlock(&batched_entropy->batch_lock);

batched_entropy = per_cpu_ptr(&batched_entropy_u64, cpu);
spin_lock(&batched_entropy->batch_lock);
batched_entropy->position = 0;
spin_unlock_irqrestore(&batched_entropy->batch_lock, flags);
}
}

/**
* randomize_page - Generate a random, page aligned address
* @start: The smallest acceptable address the caller will take.
* @range: The size of the area, starting at @start, within which the
* random address must fall.
*
* If @start + @range would overflow, @range is capped.
*
* NOTE: Historical use of randomize_range, which this replaces, presumed that
* @start was already page aligned. We now align it regardless.
*
* Return: A page aligned address within [start, start + range). On error,
* @start is returned.
*/
unsigned long randomize_page(unsigned long start, unsigned long range)
{
if (!PAGE_ALIGNED(start)) {
range -= PAGE_ALIGN(start) - start;
start = PAGE_ALIGN(start);
}

if (start > ULONG_MAX - range)
range = ULONG_MAX - start;

range >>= PAGE_SHIFT;

if (range == 0)
return start;

return start + (get_random_long() % range << PAGE_SHIFT);
}

0 comments on commit 866aae8

Please sign in to comment.