Skip to content

Commit

Permalink
Linux Random Number Generator
Browse files Browse the repository at this point in the history
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 a
per-CPU entropy pool using a hash operation that can be changed during
runtime. Per default, SHA-256 is used.

 (a) When an interrupt occurs, the high-resolution time stamp is mixed
into the per-CPU entropy pool. 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 per-CPU entropy pool. This data is not credited with
entropy by the LRNG.

 (c) Device drivers may provide data that is mixed into an auxiliary
pool using the same hash that is used to process the per-CPU entropy
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 auxiliary 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 auxiliary 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 seed data for the DRNG is to be generated, all per-CPU
entropy pools and the auxiliary pool are hashed. The message digest
forms the new auxiliary pool state. At the same time, this data
is used for seeding the DRNG.

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 per-CPU entropy pool. During boot time,
until the fully seeded stage is reached, each time stamp with its
32 least significant bits is are concatenated. When 16 such events
are received, they are injected into the per-CPU entropy pool.

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. Truncation: The received time stamps are truncated to 8 least
significant bits (or 32 least significant bits during boot time)

2. Concatenation: The received and truncated time stamps as well as
auxiliary 32 bit words are concatenated to fill the per-CPU data
array that is capable of holding 64 8-bit words.

3. Hashing: A set of concatenated time stamp data received from the
interrupts are hashed together with the current existing per-CPU
entropy pool state. The resulting message digest is the new per-CPU
entropy pool state.

4. Hashing: When new data is added to the auxiliary pool, the data
is hashed together with the auxiliary pool to form a new auxiliary
pool state.

5. Hashing: A message digest of all per-CPU entropy pools and the
auxiliary pool is calculated which forms the new auxiliary pool
state. At the same time, this message digest is used to fill the
slow noise source output buffer discussed in the following.

6. Truncation: The most-significant bits (MSB) defined by the
requested number of bits (commonly equal to the security strength
of the DRBG) or the entropy available transported with the buffer
(which is the minimum of the message digest size and the available
entropy in all entropy pools and the auxiliary pool), whatever is
smaller, are obtained from the slow noise source output buffer.

7. Concatenation: The temporary seed buffer used to seed the DRNG
is a concatenation of the slow noise source buffer, the Jitter RNG
output, the CPU noise source output, and the current time.

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 and the getrandom system
call are not processed.

The hash operation providing random data from the entropy pools will
always require that all entropy sources collectively can deliver at
least 128 entropy bits.

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.

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

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 in its fastest configuration
executes within an average 55 cycles whereas the existing
/dev/random on the same device takes about 97 cycles when measuring
the execution time of add_interrupt_randomness().

* use of almost never contended lock for hashing operation to collect
  raw entropy supporting concurrency-free use of massive parallel
  systems - worst case rate of contention is the number of DRNG
  reseeds, usually: number of NUMA nodes contentions per 5 minutes.

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

* instantiate one DRNG per NUMA node

* support for runtime switchable output DRNGs

* use of runtime-switchable hash for conditioning implementation
following widely accepted approach

* compile-time selectable collection 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: Torsten Duwe <duwe@lst.de>
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>
CC: Alexander Lobakin <alobakin@mailbox.org>
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: 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 Jul 14, 2021
1 parent 77d34a4 commit 55fdb89
Show file tree
Hide file tree
Showing 16 changed files with 3,772 additions and 1 deletion.
7 changes: 7 additions & 0 deletions MAINTAINERS
Expand Up @@ -10695,6 +10695,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
Expand Down
2 changes: 2 additions & 0 deletions drivers/char/Kconfig
Expand Up @@ -427,6 +427,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
Expand Down
9 changes: 8 additions & 1 deletion drivers/char/Makefile
Expand Up @@ -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
Expand Down
205 changes: 205 additions & 0 deletions drivers/char/lrng/Kconfig
@@ -0,0 +1,205 @@
# 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

menu "Specific DRNG seeding strategies"

config LRNG_OVERSAMPLE_ENTROPY_SOURCES
bool "Oversample entropy sources"
default n
help
When enabling this option, the entropy sources are
over-sampled with the following approach: First, the
the entropy sources are requested to provide 64 bits more
entropy than the size of the entropy buffer. For example,
if the entropy buffer is 256 bits, 320 bits of entropy
is requested to fill that buffer.

Second, the seed operation of the deterministic RNG
requests 128 bits more data from each entropy source than
the security strength of the DRNG during initialization.
A prerequisite for this operation is that the digest size
of the used hash must be at least equally large to generate
that buffer. If the prerequisite is not met, this
oversampling is not applied.

This strategy is intended to offset the asymptotic entropy
increase to reach full entropy in a buffer.

The strategy is consistent with the requirements in
NIST SP800-90C and is only enforced with fips=1.

If unsure, say N.

config LRNG_OVERSAMPLE_ES_BITS
int
default 0 if !LRNG_OVERSAMPLE_ENTROPY_SOURCES
default 64 if LRNG_OVERSAMPLE_ENTROPY_SOURCES

config LRNG_SEED_BUFFER_INIT_ADD_BITS
int
default 0 if !LRNG_OVERSAMPLE_ENTROPY_SOURCES
default 128 if LRNG_OVERSAMPLE_ENTROPY_SOURCES

endmenu # "Specific DRNG seeding strategies"

menu "Entropy Source Configuration"

comment "Interrupt Entropy Source"

choice
prompt "Continuous entropy compression boot time setting"
default LRNG_CONTINUOUS_COMPRESSION_ENABLED
help
Select the default behavior of the interrupt entropy source
continuous compression operation.

The Linux RNG collects entropy data during each interrupt.
For performance reasons, a amount of entropy data defined by
the LRNG entropy collection pool size is concatenated into
an array. When that array is filled up, a hash is calculated
to compress the entropy. That hash is calculated in
interrupt context.

In case such hash calculation in interrupt context is deemed
too time-consuming, the continuous compression operation
can be disabled. If disabled, the collection of entropy will
not trigger a hash compression operation in interrupt context.
The compression happens only when the DRNG is reseeded which is
in process context. This implies that old entropy data
collected after the last DRNG-reseed is overwritten with newer
entropy data once the collection pool is full instead of
retaining its entropy with the compression operation.

config LRNG_CONTINUOUS_COMPRESSION_ENABLED
bool "Enable continuous compression (default)"

config LRNG_CONTINUOUS_COMPRESSION_DISABLED
bool "Disable continuous compression"
endchoice

config LRNG_ENABLE_CONTINUOUS_COMPRESSION
bool
default y if LRNG_CONTINUOUS_COMPRESSION_ENABLED
default n if LRNG_CONTINUOUS_COMPRESSION_DISABLED

config LRNG_SWITCHABLE_CONTINUOUS_COMPRESSION
bool "Runtime-switchable continuous entropy compression"
help
Per default, the interrupt entropy source continuous
compression operation behavior is hard-wired into the kernel.
Enable this option to allow it to be configurable at boot time.

To modify the default behavior of the continuous
compression operation, use the kernel command line option
of lrng_sw_noise.lrng_pcpu_continuous_compression.

If unsure, say N.

choice
prompt "LRNG Entropy Collection Pool Size"
default LRNG_COLLECTION_SIZE_1024
help
Select the size of the LRNG entropy collection pool
storing data for the interrupt entropy source without
performing a compression operation. The larger the
collection size is, the faster the average interrupt
handling will be. The collection size represents the
number of bytes of the per-CPU memory used to batch
up entropy event data.

The default value is good for regular operations. Choose
larger sizes for servers that have no memory limitations.
If runtime memory is precious, choose a smaller size.

The collection size is unrelated to the entropy rate
or the amount of entropy the LRNG can process.

config LRNG_COLLECTION_SIZE_32
depends on LRNG_CONTINUOUS_COMPRESSION_ENABLED
depends on !LRNG_SWITCHABLE_CONTINUOUS_COMPRESSION
depends on !LRNG_OVERSAMPLE_ENTROPY_SOURCES
bool "32 interrupt events"

config LRNG_COLLECTION_SIZE_256
depends on !LRNG_OVERSAMPLE_ENTROPY_SOURCES
bool "256 interrupt events"

config LRNG_COLLECTION_SIZE_512
bool "512 interrupt events"

config LRNG_COLLECTION_SIZE_1024
bool "1024 interrupt events (default)"

config LRNG_COLLECTION_SIZE_2048
bool "2048 interrupt events"

config LRNG_COLLECTION_SIZE_4096
bool "4096 interrupt events"

config LRNG_COLLECTION_SIZE_8192
bool "8192 interrupt events"

endchoice

config LRNG_COLLECTION_SIZE
int
default 32 if LRNG_COLLECTION_SIZE_32
default 256 if LRNG_COLLECTION_SIZE_256
default 512 if LRNG_COLLECTION_SIZE_512
default 1024 if LRNG_COLLECTION_SIZE_1024
default 2048 if LRNG_COLLECTION_SIZE_2048
default 4096 if LRNG_COLLECTION_SIZE_4096
default 8192 if LRNG_COLLECTION_SIZE_8192

config LRNG_IRQ_ENTROPY_RATE
int "Interrupt Entropy Source Entropy Rate"
range 256 4294967295
default 256
help
The LRNG will collect the configured number of interrupts to
obtain 256 bits of entropy. This value can be set to any between
256 and 4294967295. The LRNG guarantees that this value is not
lower than 256. This lower limit implies that one interrupt event
is credited with one bit of entropy. This value is subject to the
increase by the oversampling factor, if no high-resolution timer
is found.

In order to effectively disable the interrupt entropy source,
the option has to be set to 4294967295. In this case, the
interrupt entropy source will still deliver data but without
being credited with entropy.

comment "CPU Entropy Source"

config LRNG_CPU_ENTROPY_RATE
int "CPU Entropy Source Entropy Rate"
range 0 256
default 8
help
The option defines the amount of entropy the LRNG applies to 256
bits of data obtained from the CPU entropy source. The LRNG
enforces the limit that this value must be in the range between
0 and 256.

In order to disable the CPU entropy source, the option has to
be set to 0.

Note, this option is overwritten when the option
CONFIG_RANDOM_TRUST_CPU is set.

endmenu # "Entropy Source Configuration"

endif # LRNG
9 changes: 9 additions & 0 deletions drivers/char/lrng/Makefile
@@ -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
91 changes: 91 additions & 0 deletions drivers/char/lrng/lrng_archrandom.c
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
/*
* LRNG Fast Entropy Source: CPU-based entropy source
*
* Copyright (C) 2016 - 2021, 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 CONFIG_LRNG_CPU_ENTROPY_RATE
#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
#ifdef CONFIG_LRNG_RUNTIME_ES_CONFIG
module_param(archrandom, uint, 0644);
MODULE_PARM_DESC(archrandom, "Entropy in bits of 256 data bits from CPU noise source (e.g. RDRAND)");
#endif

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;
lrng_pool_add_entropy();
} else {
archrandom = LRNG_ARCHRANDOM_DEFAULT_STRENGTH;
}

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

u32 lrng_archrandom_entropylevel(u32 requested_bits)
{
return lrng_fast_noise_entropylevel(archrandom, requested_bits);
}

/**
* 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 requested_bits)
{
u32 i, ent_bits = lrng_archrandom_entropylevel(requested_bits);

/* operate on full blocks */
BUILD_BUG_ON(LRNG_DRNG_SECURITY_STRENGTH_BYTES % sizeof(unsigned long));
BUILD_BUG_ON(CONFIG_LRNG_SEED_BUFFER_INIT_ADD_BITS %
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 < (requested_bits >> 3);
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;
}
}

pr_debug("obtained %u bits of entropy from CPU RNG noise source\n",
ent_bits);
return ent_bits;
}

0 comments on commit 55fdb89

Please sign in to comment.