2021-07-14 05:52:36

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v41 00/13] /dev/random - a new approach

Hi,

The following patch set provides a different approach to /dev/random which
is called Linux Random Number Generator (LRNG) to collect entropy within
the Linux kernel. It provides the same API and ABI and can be used as a
drop-in replacement. A general overview is given with [6].

The LRNG implements at least all features of the existing /dev/random such as
NUMA-node-local DRNGs. Patches 1 through 3 provide the code that is feature-
identical. The following advantages compared to the existing /dev/random
implementation are present:

* Sole use of crypto for data processing:

- Exclusive use of a hash operation for conditioning entropy data with
a clear mathematical description as given in [2] section 2.2 -
non-cryptographic operations like LFSR are not used.

- The LRNG uses only properly defined and implemented cryptographic
algorithms unlike the use of the SHA-1 transformation in the existing
/dev/random implementation.

- Hash operations use NUMA-node-local hash instances to benefit large
parallel systems.

- LRNG uses limited number of data post-processing steps as documented in
[2] section 2.2 compared to the large variation of different
post-processing steps in the existing /dev/random implementation that
have no apparent mathematical description (see [2] section 4.5).

* Performance

- Faster by up to 130% in the critical code path of the interrupt handler
depending on data collection size configurable at kernel compile time -
the default is now set such that the highest performance is achieved as
outlined in [2] section 4.2.

- Configurable data collection sizes to accommodate small environments
and big environments via CONFIG_LRNG_COLLECTION_SIZE.

- Entropy collection using an almost never contended lock to benefit
large parallel systems – worst case rate of contention is the number
of DRNG reseeds, usually the number of potential contentions per 10
minutes is equal to number of NUMA nodes.

- ChaCha20 DRNG is significantly faster as implemented in the existing
/dev/random as demonstrated with [2] table 2.

- Faster entropy collection during boot time to reach fully seeded
level, including on virtual systems or systems with SSDs as outlined
in [2] section 4.1.

- Faster processing of external data added to LRNG via /dev/random
or add_hwgenerator_randomness.

* Testing

- Availability of run-time health tests of the raw unconditioned
noise source to identify degradation of the available entropy as
documented in [2] section 2.5.4. Such health tests are important
today due to virtual machine monitors reducing the resolution of
or disabling the high-resolution timer.

- Heuristic entropy estimation is based on quantitative measurements
and analysis following SP800-90B and not on coincidental
underestimation of entropy applied by the existing /dev/random as
outlined in [4] section 4.4.

- Power-on self tests for critical deterministic components (ChaCha20
DRNG, software hash implementation, and entropy collection logic)
not already covered by power-up tests of the kernel crypto API as
documented in [2] section 2.14.

- Availability of test interfaces for all operational stages of the
LRNG including boot-time raw entropy event data sampling as outlined
in [2] section 2.15.

- Fully testable ChaCha20 DRNG via a userspace ChaCha20 DRNG
implementation [3].

- In case of using the kernel crypto API SHASH hash implementation, it
is fully testable and tested via the NIST ACVP test framework, for
example certificates A734, A737, and A738.

- The LRNG offers a test interface to validate the used software hash
implementation and in particular that the LRNG invokes the hash
correctly, allowing a NIST ACVP-compliant test cycle - see [2]
section 2.15.

- Availability of stress testing covering the different code paths for
data and mechanism (de)allocations and code paths covered with locks.

- Availability of regression tests verifying the different options provided
with the LRNG.

* Entropy collection

- The LRNG is shipped with test tools allowing the collection of
raw unconditioned entropy during runtime and boot time available at
[1].

- Full entropy assessment and description is provided with [2] chapter 3,
specifically section 3.2.6.

- Guarantee that entropy events are not credited with entropy twice
(the existing /dev/random implementation credits HID/disk and
interrupt events with entropy which are a derivative of each other).

* Configurable

- LRNG kernel configuration allows configuration that is functionally
equivalent to the existing /dev/random. Non-compiled additional code
is folded into no-ops.

- The following additional functions are compile-time selectable
independent of each other:

+ Enabling of switchable cryptographic implementation support. This
allows enabling an SP800-90A DRBG.

+ Enabling of using Jitter RNG noise source.

+ Enabling of noise source health tests.

+ Enabling of test interface allowing to enable each test interface
individually.

+ Enabling of the power-up self test.

- At boot-time, the SP800-90B health tests can be enabled as outlined
in [2] section 2.5.4.

- At boot-time, the entropy rate used to credit the external CPU-based
noise source and Jitter RNG noise source can be configured including
setting an entropy rate of zero or full entropy - see [2] sections
2.5.2 and 2.5.3.

* Run-time pluggable cryptographic implementations used for all data
processing steps specified in [2] section 2.2

- The DRNG can be replaced with a different implementation allowing
any type of DRNG to provide data via the output interfaces. The LRNG
provides the following types of DRNG implementations:

+ ChaCha20-based software implementation that is used per default.

+ SP800-90A DRBG using accelerated cryptographic implementations that
may sleep.

+ Any DRNG that is accessible via the kernel crypto API RNG subsystem.

- The hash component can be replaced with any other hash implementation
provided the implementation does not sleep. The LRNG provides the
access to the following types of non-sleeping hash implementations:

+ SHA-256 software implementation that is used per default. Due to
kernel build system inconsistencies, the software SHA-1 implementation
is used if the kernel crypto API is not compiled.

+ SHA-512 hash using the fastest hash implementation available via the
kernel crypto API SHASH subsystem.

* Code structure

- The LRNG source code is available for current upstream Linux kernel
separate to the existing /dev/random which means that users who are
conservative can use the unchanged existing /dev/random implementation.

- Back-port patches are available at [5] to apply the LRNG to Linux
kernel versions of 5.10, 5.8, 5.4, 4.19, 4.14, 4.12, 4.10, and 4.4. Patches
for other kernel versions are easily derived from the existing ones.

Booting the patch with the kernel command line option
"dyndbg=file drivers/char/lrng/* +p" generates logs indicating the
operation of the LRNG. Each log is pre-pended with "lrng".

An entropy analysis is performed on the following systems - details
are given in [2] appendix C:

* x86 KVM virtualized guest 32 and 64 bit systems

* x86 bare metal

* older and newer ARMv7 system

* ARM64

* POWER7 LE and POWER 8 BE

* IBM Z System mainframe

* old MIPS embedded device

* testing with GCC and Clang

[1] https://www.chronox.de/lrng.html - If the patch is accepted, I would
be volunteering to convert the documentation into RST format and
contribute it to the Linux kernel documentation directory.

[2] https://www.chronox.de/lrng/doc/lrng.pdf

[3] https://www.chronox.de/chacha20_drng.html

[4] https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/Studies/LinuxRNG/LinuxRNG_EN_V4_1.pdf

[5] https://github.com/smuellerDD/lrng/tree/master/backports

[6] https://www.chronox.de/lrng/doc/lrng_presentation_v41.pdf

Changes (compared to the previous patch set) - individual patches
are visible at https://github.com/smuellerDD/lrng/commits/master:

- update seeding threshold when loading DRNG only if min seeded

- insert bootloader entropy directly into the aux pool

- RNDADDTOENTCNT - entropy estmate update of aux pool

- cleanup of initial seeding

- use work queue after initialization only

- add_random_ready_callback ping after full initialization

- zeroize seed buffer

- invoke processing of ready_list only if fully seeded

- invoke invalidate_batched_entropy after initialization

- always fill in the time stamp into seed buffer

- initialize entropy value if insufficient entropy available

- signal end of boot cycle in non-NUMA configuration

- set NUMA node online flag in proper condition

- harden entropy source configuration

- significantly enhance performance of aux pool

- fix LRNG reseed locking

- set LRNG to non-operational for 90C compliance

- fix increment of ChaCha20 nonce

- make switch functions a noop if switching is disabled

CC: Torsten Duwe <[email protected]>
CC: "Eric W. Biederman" <[email protected]>
CC: "Alexander E. Patrakov" <[email protected]>
CC: "Ahmed S. Darwish" <[email protected]>
CC: "Theodore Y. Ts'o" <[email protected]>
CC: Willy Tarreau <[email protected]>
CC: Matthew Garrett <[email protected]>
CC: Vito Caputo <[email protected]>
CC: Andreas Dilger <[email protected]>
CC: Jan Kara <[email protected]>
CC: Ray Strode <[email protected]>
CC: William Jon McCann <[email protected]>
CC: zhangjs <[email protected]>
CC: Andy Lutomirski <[email protected]>
CC: Florian Weimer <[email protected]>
CC: Lennart Poettering <[email protected]>
CC: Nicolai Stange <[email protected]>
CC: Eric Biggers <[email protected]>
CC: Alexander Lobakin <[email protected]>
Tested-by: Marcelo Henrique Cerri <[email protected]>

Stephan Mueller (13):
Linux Random Number Generator
LRNG - allocate one DRNG instance per NUMA node
LRNG - sysctls and /proc interface
LRNG - add switchable DRNG support
LRNG - add common generic hash support
crypto: DRBG - externalize DRBG functions for LRNG
LRNG - add SP800-90A DRBG extension
LRNG - add kernel crypto API PRNG extension
crypto: provide access to a static Jitter RNG state
LRNG - add Jitter RNG fast noise source
LRNG - add SP800-90B compliant health tests
LRNG - add interface for gathering of raw entropy
LRNG - add power-on and runtime self-tests

MAINTAINERS | 7 +
crypto/drbg.c | 16 +-
crypto/jitterentropy-kcapi.c | 3 +-
crypto/jitterentropy.c | 31 +-
drivers/char/Kconfig | 2 +
drivers/char/Makefile | 9 +-
drivers/char/lrng/Kconfig | 515 +++++++++++++
drivers/char/lrng/Makefile | 20 +
drivers/char/lrng/lrng_archrandom.c | 91 +++
drivers/char/lrng/lrng_aux.c | 136 ++++
drivers/char/lrng/lrng_chacha20.c | 321 ++++++++
drivers/char/lrng/lrng_chacha20.h | 29 +
drivers/char/lrng/lrng_drbg.c | 198 +++++
drivers/char/lrng/lrng_drng.c | 422 +++++++++++
drivers/char/lrng/lrng_health.c | 410 ++++++++++
drivers/char/lrng/lrng_interfaces.c | 648 ++++++++++++++++
drivers/char/lrng/lrng_internal.h | 425 +++++++++++
drivers/char/lrng/lrng_jent.c | 90 +++
drivers/char/lrng/lrng_kcapi.c | 227 ++++++
drivers/char/lrng/lrng_kcapi_hash.c | 103 +++
drivers/char/lrng/lrng_kcapi_hash.h | 20 +
drivers/char/lrng/lrng_numa.c | 122 +++
drivers/char/lrng/lrng_pool.c | 622 ++++++++++++++++
drivers/char/lrng/lrng_proc.c | 185 +++++
drivers/char/lrng/lrng_selftest.c | 351 +++++++++
drivers/char/lrng/lrng_sw_noise.c | 702 ++++++++++++++++++
drivers/char/lrng/lrng_sw_noise.h | 71 ++
drivers/char/lrng/lrng_switch.c | 231 ++++++
drivers/char/lrng/lrng_testing.c | 689 +++++++++++++++++
include/crypto/drbg.h | 7 +
.../crypto/internal}/jitterentropy.h | 3 +
include/linux/lrng.h | 81 ++
32 files changed, 6777 insertions(+), 10 deletions(-)
create mode 100644 drivers/char/lrng/Kconfig
create mode 100644 drivers/char/lrng/Makefile
create mode 100644 drivers/char/lrng/lrng_archrandom.c
create mode 100644 drivers/char/lrng/lrng_aux.c
create mode 100644 drivers/char/lrng/lrng_chacha20.c
create mode 100644 drivers/char/lrng/lrng_chacha20.h
create mode 100644 drivers/char/lrng/lrng_drbg.c
create mode 100644 drivers/char/lrng/lrng_drng.c
create mode 100644 drivers/char/lrng/lrng_health.c
create mode 100644 drivers/char/lrng/lrng_interfaces.c
create mode 100644 drivers/char/lrng/lrng_internal.h
create mode 100644 drivers/char/lrng/lrng_jent.c
create mode 100644 drivers/char/lrng/lrng_kcapi.c
create mode 100644 drivers/char/lrng/lrng_kcapi_hash.c
create mode 100644 drivers/char/lrng/lrng_kcapi_hash.h
create mode 100644 drivers/char/lrng/lrng_numa.c
create mode 100644 drivers/char/lrng/lrng_pool.c
create mode 100644 drivers/char/lrng/lrng_proc.c
create mode 100644 drivers/char/lrng/lrng_selftest.c
create mode 100644 drivers/char/lrng/lrng_sw_noise.c
create mode 100644 drivers/char/lrng/lrng_sw_noise.h
create mode 100644 drivers/char/lrng/lrng_switch.c
create mode 100644 drivers/char/lrng/lrng_testing.c
rename {crypto => include/crypto/internal}/jitterentropy.h (84%)
create mode 100644 include/linux/lrng.h

--
2.31.1





2021-07-14 05:52:39

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v41 03/13] LRNG - sysctls and /proc interface

The LRNG sysctl interface provides the same controls as the existing
/dev/random implementation. These sysctls behave identically and are
implemented identically. The goal is to allow a possible merge of the
existing /dev/random implementation with this implementation which
implies that this patch tries have a very close similarity. Yet, all
sysctls are documented at [1].

In addition, it provides the file lrng_type which provides details about
the LRNG:

- the name of the DRNG that produces the random numbers for /dev/random,
/dev/urandom, getrandom(2)

- the hash used to produce random numbers from the entropy pool

- the number of secondary DRNG instances

- indicator whether the LRNG operates SP800-90B compliant

- indicator whether a high-resolution timer is identified - only with a
high-resolution timer the interrupt noise source will deliver sufficient
entropy

- indicator whether the LRNG has been minimally seeded (i.e. is the
secondary DRNG seeded with at least 128 bits of entropy)

- indicator whether the LRNG has been fully seeded (i.e. is the
secondary DRNG seeded with at least 256 bits of entropy)

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

CC: Torsten Duwe <[email protected]>
CC: "Eric W. Biederman" <[email protected]>
CC: "Alexander E. Patrakov" <[email protected]>
CC: "Ahmed S. Darwish" <[email protected]>
CC: "Theodore Y. Ts'o" <[email protected]>
CC: Willy Tarreau <[email protected]>
CC: Matthew Garrett <[email protected]>
CC: Vito Caputo <[email protected]>
CC: Andreas Dilger <[email protected]>
CC: Jan Kara <[email protected]>
CC: Ray Strode <[email protected]>
CC: William Jon McCann <[email protected]>
CC: zhangjs <[email protected]>
CC: Andy Lutomirski <[email protected]>
CC: Florian Weimer <[email protected]>
CC: Lennart Poettering <[email protected]>
CC: Nicolai Stange <[email protected]>
CC: Alexander Lobakin <[email protected]>
Reviewed-by: Marcelo Henrique Cerri <[email protected]>
Reviewed-by: Roman Drahtmueller <[email protected]>
Tested-by: Marcelo Henrique Cerri <[email protected]>
Tested-by: Neil Horman <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_interfaces.c | 2 -
drivers/char/lrng/lrng_internal.h | 4 +
drivers/char/lrng/lrng_proc.c | 185 ++++++++++++++++++++++++++++
4 files changed, 190 insertions(+), 2 deletions(-)
create mode 100644 drivers/char/lrng/lrng_proc.c

diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile
index 29724c65287d..ac97f0b11cb7 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -9,3 +9,4 @@ obj-y += lrng_pool.o lrng_aux.o \
lrng_interfaces.o

obj-$(CONFIG_NUMA) += lrng_numa.o
+obj-$(CONFIG_SYSCTL) += lrng_proc.o
diff --git a/drivers/char/lrng/lrng_interfaces.c b/drivers/char/lrng/lrng_interfaces.c
index 3a56bfdca01e..4b8c56849112 100644
--- a/drivers/char/lrng/lrng_interfaces.c
+++ b/drivers/char/lrng/lrng_interfaces.c
@@ -38,8 +38,6 @@ static DECLARE_WAIT_QUEUE_HEAD(lrng_write_wait);
static DECLARE_WAIT_QUEUE_HEAD(lrng_init_wait);
static struct fasync_struct *fasync;

-struct ctl_table random_table[];
-
/********************************** Helper ***********************************/

/* Is the DRNG seed level too low? */
diff --git a/drivers/char/lrng/lrng_internal.h b/drivers/char/lrng/lrng_internal.h
index e1d83c888894..a4ed142d11eb 100644
--- a/drivers/char/lrng/lrng_internal.h
+++ b/drivers/char/lrng/lrng_internal.h
@@ -114,7 +114,11 @@ void lrng_cc20_init_state(struct chacha20_state *state);

/********************************** /proc *************************************/

+#ifdef CONFIG_SYSCTL
+void lrng_pool_inc_numa_node(void);
+#else
static inline void lrng_pool_inc_numa_node(void) { }
+#endif

/****************************** LRNG interfaces *******************************/

diff --git a/drivers/char/lrng/lrng_proc.c b/drivers/char/lrng/lrng_proc.c
new file mode 100644
index 000000000000..30bc0a1a6632
--- /dev/null
+++ b/drivers/char/lrng/lrng_proc.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG proc and sysctl interfaces
+ *
+ * Copyright (C) 2016 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#include <linux/lrng.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/sysctl.h>
+#include <linux/uuid.h>
+
+#include "lrng_internal.h"
+#include "lrng_sw_noise.h"
+
+/*
+ * This function is used to return both the bootid UUID, and random
+ * UUID. The difference is in whether table->data is NULL; if it is,
+ * then a new UUID is generated and returned to the user.
+ *
+ * If the user accesses this via the proc interface, the UUID will be
+ * returned as an ASCII string in the standard UUID format; if via the
+ * sysctl system call, as 16 bytes of binary data.
+ */
+static int lrng_proc_do_uuid(struct ctl_table *table, int write,
+ void *buffer, size_t *lenp, loff_t *ppos)
+{
+ struct ctl_table fake_table;
+ unsigned char buf[64], tmp_uuid[16], *uuid;
+
+ uuid = table->data;
+ if (!uuid) {
+ uuid = tmp_uuid;
+ generate_random_uuid(uuid);
+ } else {
+ static DEFINE_SPINLOCK(bootid_spinlock);
+
+ spin_lock(&bootid_spinlock);
+ if (!uuid[8])
+ generate_random_uuid(uuid);
+ spin_unlock(&bootid_spinlock);
+ }
+
+ sprintf(buf, "%pU", uuid);
+
+ fake_table.data = buf;
+ fake_table.maxlen = sizeof(buf);
+
+ return proc_dostring(&fake_table, write, buffer, lenp, ppos);
+}
+
+static int lrng_proc_do_entropy(struct ctl_table *table, int write,
+ void *buffer, size_t *lenp, loff_t *ppos)
+{
+ struct ctl_table fake_table;
+ int entropy_count;
+
+ entropy_count = lrng_avail_entropy();
+
+ fake_table.data = &entropy_count;
+ fake_table.maxlen = sizeof(entropy_count);
+
+ return proc_dointvec(&fake_table, write, buffer, lenp, ppos);
+}
+
+static int lrng_proc_do_poolsize(struct ctl_table *table, int write,
+ void *buffer, size_t *lenp, loff_t *ppos)
+{
+ struct ctl_table fake_table;
+ int entropy_count;
+
+ /* LRNG can at most retain entropy in per-CPU pools and aux pool */
+ entropy_count = lrng_get_digestsize() + lrng_pcpu_avail_pool_size();
+
+ fake_table.data = &entropy_count;
+ fake_table.maxlen = sizeof(entropy_count);
+
+ return proc_dointvec(&fake_table, write, buffer, lenp, ppos);
+}
+
+static int lrng_min_write_thresh;
+static int lrng_max_write_thresh = LRNG_MAX_DIGESTSIZE;
+static char lrng_sysctl_bootid[16];
+static int lrng_drng_reseed_max_min;
+
+struct ctl_table random_table[] = {
+ {
+ .procname = "poolsize",
+ .maxlen = sizeof(int),
+ .mode = 0444,
+ .proc_handler = lrng_proc_do_poolsize,
+ },
+ {
+ .procname = "entropy_avail",
+ .maxlen = sizeof(int),
+ .mode = 0444,
+ .proc_handler = lrng_proc_do_entropy,
+ },
+ {
+ .procname = "write_wakeup_threshold",
+ .data = &lrng_write_wakeup_bits,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &lrng_min_write_thresh,
+ .extra2 = &lrng_max_write_thresh,
+ },
+ {
+ .procname = "boot_id",
+ .data = &lrng_sysctl_bootid,
+ .maxlen = 16,
+ .mode = 0444,
+ .proc_handler = lrng_proc_do_uuid,
+ },
+ {
+ .procname = "uuid",
+ .maxlen = 16,
+ .mode = 0444,
+ .proc_handler = lrng_proc_do_uuid,
+ },
+ {
+ .procname = "urandom_min_reseed_secs",
+ .data = &lrng_drng_reseed_max_time,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &lrng_drng_reseed_max_min,
+ },
+ { }
+};
+
+/* Number of online DRNGs */
+static u32 numa_drngs = 1;
+
+void lrng_pool_inc_numa_node(void)
+{
+ numa_drngs++;
+}
+
+static int lrng_proc_type_show(struct seq_file *m, void *v)
+{
+ struct lrng_drng *lrng_drng_init = lrng_drng_init_instance();
+ unsigned long flags = 0;
+ unsigned char buf[390];
+
+ lrng_drng_lock(lrng_drng_init, &flags);
+ snprintf(buf, sizeof(buf),
+ "DRNG name: %s\n"
+ "Hash for reading entropy pool: %s\n"
+ "Hash for operating aux entropy pool: %s\n"
+ "LRNG security strength in bits: %d\n"
+ "per-CPU interrupt collection size: %u\n"
+ "number of DRNG instances: %u\n"
+ "Standards compliance: %s%s\n"
+ "High-resolution timer: %s\n"
+ "LRNG minimally seeded: %s\n"
+ "LRNG fully seeded: %s\n"
+ "Continuous compression: %s\n",
+ lrng_drng_init->crypto_cb->lrng_drng_name(),
+ lrng_drng_init->crypto_cb->lrng_hash_name(),
+ lrng_drng_init->crypto_cb->lrng_hash_name(),
+ lrng_security_strength(),
+ LRNG_DATA_NUM_VALUES,
+ numa_drngs,
+ lrng_sp80090b_compliant() ? "SP800-90B " : "",
+ lrng_sp80090c_compliant() ? "SP800-90C " : "",
+ lrng_pool_highres_timer() ? "true" : "false",
+ lrng_state_min_seeded() ? "true" : "false",
+ lrng_state_fully_seeded() ? "true" : "false",
+ lrng_pcpu_continuous_compression_state() ? "true" : "false");
+ lrng_drng_unlock(lrng_drng_init, &flags);
+
+ seq_write(m, buf, strlen(buf));
+
+ return 0;
+}
+
+static int __init lrng_proc_type_init(void)
+{
+ proc_create_single("lrng_type", 0444, NULL, &lrng_proc_type_show);
+ return 0;
+}
+
+module_init(lrng_proc_type_init);
--
2.31.1




2021-07-14 05:52:38

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v41 11/13] LRNG - add SP800-90B compliant health tests

Implement health tests for LRNG's slow noise sources as mandated by
SP-800-90B The file contains the following health tests:

- stuck test: The stuck test calculates the first, second and third
discrete derivative of the time stamp to be processed by the hash
for the per-CPU entropy pool. Only if all three values are non-zero,
the received time delta is considered to be non-stuck.

- SP800-90B Repetition Count Test (RCT): The LRNG uses an enhanced
version of the RCT specified in SP800-90B section 4.4.1. Instead of
counting identical back-to-back values, the input to the RCT is the
counting of the stuck values during the processing of received
interrupt events. The RCT is applied with alpha=2^-30 compliant to
the recommendation of FIPS 140-2 IG 9.8. During the counting operation,
the LRNG always calculates the RCT cut-off value of C. If that value
exceeds the allowed cut-off value, the LRNG will trigger the health
test failure discussed below. An error is logged to the kernel log
that such RCT failure occurred. This test is only applied and
enforced in FIPS mode, i.e. when the kernel compiled with
CONFIG_CONFIG_FIPS is started with fips=1.

- SP800-90B Adaptive Proportion Test (APT): The LRNG implements the
APT as defined in SP800-90B section 4.4.2. The applied significance
level again is alpha=2^-30 compliant to the recommendation of FIPS
140-2 IG 9.8.

The aforementioned health tests are applied to the first 1,024 time stamps
obtained from interrupt events. In case one error is identified for either
the RCT, or the APT, the collected entropy is invalidated and the
SP800-90B startup health test is restarted.

As long as the SP800-90B startup health test is not completed, all LRNG
random number output interfaces that may block will block and not generate
any data. This implies that only those potentially blocking interfaces are
defined to provide random numbers that are seeded with the interrupt noise
source being SP800-90B compliant. All other output interfaces will not be
affected by the SP800-90B startup test and thus are not considered
SP800-90B compliant.

At runtime, the SP800-90B APT and RCT are applied to each time stamp
generated for a received interrupt. When either the APT and RCT indicates
a noise source failure, the LRNG is reset to a state it has immediately
after boot:

- all entropy counters are set to zero

- the SP800-90B startup tests are re-performed which implies that
getrandom(2) would block again until new entropy was collected

To summarize, the following rules apply:

• SP800-90B compliant output interfaces

- /dev/random

- getrandom(2) system call

- get_random_bytes kernel-internal interface when being triggered by
the callback registered with add_random_ready_callback

• SP800-90B non-compliant output interfaces

- /dev/urandom

- get_random_bytes kernel-internal interface called directly

- randomize_page kernel-internal interface

- get_random_u32 and get_random_u64 kernel-internal interfaces

- get_random_u32_wait, get_random_u64_wait, get_random_int_wait, and
get_random_long_wait kernel-internal interfaces

If either the RCT, or the APT health test fails irrespective whether
during initialization or runtime, the following actions occur:

1. The entropy of the entire entropy pool is invalidated.

2. All DRNGs are reset which imply that they are treated as being
not seeded and require a reseed during next invocation.

3. The SP800-90B startup health test are initiated with all
implications of the startup tests. That implies that from that point
on, new events must be observed and its entropy must be inserted into
the entropy pool before random numbers are calculated from the
entropy pool.

Further details on the SP800-90B compliance and the availability of all
test tools required to perform all tests mandated by SP800-90B are
provided at [1].

The entire health testing code is compile-time configurable.

The patch provides a CONFIG_BROKEN configuration of the APT / RCT cutoff
values which have a high likelihood to trigger the health test failure.
The BROKEN APT cutoff is set to the exact mean of the expected value if
the time stamps are equally distributed (512 time stamps divided by 16
possible values due to using the 4 LSB of the time stamp). The BROKEN
RCT cutoff value is set to 1 which is likely to be triggered during
regular operation.

CC: Torsten Duwe <[email protected]>
CC: "Eric W. Biederman" <[email protected]>
CC: "Alexander E. Patrakov" <[email protected]>
CC: "Ahmed S. Darwish" <[email protected]>
CC: "Theodore Y. Ts'o" <[email protected]>
CC: Willy Tarreau <[email protected]>
CC: Matthew Garrett <[email protected]>
CC: Vito Caputo <[email protected]>
CC: Andreas Dilger <[email protected]>
CC: Jan Kara <[email protected]>
CC: Ray Strode <[email protected]>
CC: William Jon McCann <[email protected]>
CC: zhangjs <[email protected]>
CC: Andy Lutomirski <[email protected]>
CC: Florian Weimer <[email protected]>
CC: Lennart Poettering <[email protected]>
CC: Nicolai Stange <[email protected]>
CC: Alexander Lobakin <[email protected]>
Reviewed-by: Roman Drahtmueller <[email protected]>
Tested-by: Marcelo Henrique Cerri <[email protected]>
Tested-by: Neil Horman <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Kconfig | 56 +++++
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_health.c | 410 ++++++++++++++++++++++++++++++++
3 files changed, 467 insertions(+)
create mode 100644 drivers/char/lrng/lrng_health.c

diff --git a/drivers/char/lrng/Kconfig b/drivers/char/lrng/Kconfig
index e622b8532e2b..931959f75a83 100644
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -164,6 +164,62 @@ config LRNG_COLLECTION_SIZE
default 4096 if LRNG_COLLECTION_SIZE_4096
default 8192 if LRNG_COLLECTION_SIZE_8192

+config LRNG_HEALTH_TESTS
+ bool "Enable noise source online health tests"
+ help
+ The online health tests validate the noise source at
+ runtime for fatal errors. These tests include SP800-90B
+ compliant tests which are invoked if the system is booted
+ with fips=1. In case of fatal errors during active
+ SP800-90B tests, the issue is logged and the noise
+ data is discarded. These tests are required for full
+ compliance with SP800-90B.
+
+ If unsure, say Y.
+
+config LRNG_RCT_BROKEN
+ bool "SP800-90B RCT with dangerous low cutoff value"
+ depends on LRNG_HEALTH_TESTS
+ depends on BROKEN
+ default n
+ help
+ This option enables a dangerously low SP800-90B repetitive
+ count test (RCT) cutoff value which makes it very likely
+ that the RCT is triggered to raise a self test failure.
+
+ This option is ONLY intended for developers wanting to
+ test the effectiveness of the SP800-90B RCT health test.
+
+ If unsure, say N.
+
+config LRNG_APT_BROKEN
+ bool "SP800-90B APT with dangerous low cutoff value"
+ depends on LRNG_HEALTH_TESTS
+ depends on BROKEN
+ default n
+ help
+ This option enables a dangerously low SP800-90B adaptive
+ proportion test (APT) cutoff value which makes it very
+ likely that the APT is triggered to raise a self test
+ failure.
+
+ This option is ONLY intended for developers wanting to
+ test the effectiveness of the SP800-90B APT health test.
+
+ If unsure, say N.
+
+# Default taken from SP800-90B sec 4.4.1 - significance level 2^-30
+config LRNG_RCT_CUTOFF
+ int
+ default 31 if !LRNG_RCT_BROKEN
+ default 1 if LRNG_RCT_BROKEN
+
+# Default taken from SP800-90B sec 4.4.2 - significance level 2^-30
+config LRNG_APT_CUTOFF
+ int
+ default 325 if !LRNG_APT_BROKEN
+ default 32 if LRNG_APT_BROKEN
+
config LRNG_IRQ_ENTROPY_RATE
int "Interrupt Entropy Source Entropy Rate"
range 256 4294967295
diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile
index 6be88156010a..3c8637befb42 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -15,3 +15,4 @@ obj-$(CONFIG_LRNG_KCAPI_HASH) += lrng_kcapi_hash.o
obj-$(CONFIG_LRNG_DRBG) += lrng_drbg.o
obj-$(CONFIG_LRNG_KCAPI) += lrng_kcapi.o
obj-$(CONFIG_LRNG_JENT) += lrng_jent.o
+obj-$(CONFIG_LRNG_HEALTH_TESTS) += lrng_health.o
diff --git a/drivers/char/lrng/lrng_health.c b/drivers/char/lrng/lrng_health.c
new file mode 100644
index 000000000000..a56a0c4261ab
--- /dev/null
+++ b/drivers/char/lrng/lrng_health.c
@@ -0,0 +1,410 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Linux Random Number Generator (LRNG) Health Testing
+ *
+ * Copyright (C) 2019 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/fips.h>
+#include <linux/module.h>
+
+#include "lrng_internal.h"
+
+/* Stuck Test */
+struct lrng_stuck_test {
+ u32 last_time; /* Stuck test: time of previous IRQ */
+ u32 last_delta; /* Stuck test: delta of previous IRQ */
+ u32 last_delta2; /* Stuck test: 2. time derivation of prev IRQ */
+};
+
+/* Repetition Count Test */
+struct lrng_rct {
+ atomic_t rct_count; /* Number of stuck values */
+};
+
+/* Adaptive Proportion Test */
+struct lrng_apt {
+ /* Data window size */
+#define LRNG_APT_WINDOW_SIZE 512
+ /* LSB of time stamp to process */
+#define LRNG_APT_LSB 16
+#define LRNG_APT_WORD_MASK (LRNG_APT_LSB - 1)
+ atomic_t apt_count; /* APT counter */
+ atomic_t apt_base; /* APT base reference */
+
+ atomic_t apt_trigger;
+ bool apt_base_set; /* Is APT base set? */
+};
+
+/* The health test code must operate lock-less */
+struct lrng_health {
+ struct lrng_rct rct;
+ struct lrng_apt apt;
+
+ bool health_test_enabled;
+
+ /* SP800-90B startup health tests */
+#define LRNG_SP80090B_STARTUP_SAMPLES 1024
+#define LRNG_SP80090B_STARTUP_BLOCKS ((LRNG_SP80090B_STARTUP_SAMPLES + \
+ LRNG_APT_WINDOW_SIZE - 1) / \
+ LRNG_APT_WINDOW_SIZE)
+ bool sp80090b_startup_done;
+ atomic_t sp80090b_startup_blocks;
+};
+
+static struct lrng_health lrng_health = {
+ .rct.rct_count = ATOMIC_INIT(0),
+
+ .apt.apt_count = ATOMIC_INIT(0),
+ .apt.apt_base = ATOMIC_INIT(-1),
+ .apt.apt_trigger = ATOMIC_INIT(LRNG_APT_WINDOW_SIZE),
+ .apt.apt_base_set = false,
+
+ .health_test_enabled = true,
+
+ .sp80090b_startup_blocks = ATOMIC_INIT(LRNG_SP80090B_STARTUP_BLOCKS),
+ .sp80090b_startup_done = false,
+};
+
+static DEFINE_PER_CPU(struct lrng_stuck_test, lrng_stuck_test);
+
+static inline bool lrng_sp80090b_health_requested(void)
+{
+ /* Health tests are only requested in FIPS mode */
+ return fips_enabled;
+}
+
+static inline bool lrng_sp80090b_health_enabled(void)
+{
+ struct lrng_health *health = &lrng_health;
+
+ return lrng_sp80090b_health_requested() && health->health_test_enabled;
+}
+
+/***************************************************************************
+ * SP800-90B Compliance
+ *
+ * If the Linux-RNG is booted into FIPS mode, the following interfaces
+ * provide an SP800-90B compliant noise source:
+ *
+ * * /dev/random
+ * * getrandom(2)
+ * * get_random_bytes when using it in conjunction with
+ * add_random_ready_callback
+ *
+ * All other interfaces, including /dev/urandom or get_random_bytes without
+ * the add_random_ready_callback cannot claim to use an SP800-90B compliant
+ * noise source.
+ ***************************************************************************/
+
+/*
+ * Perform SP800-90B startup testing
+ */
+static inline void lrng_sp80090b_startup(struct lrng_health *health)
+{
+ if (!health->sp80090b_startup_done &&
+ atomic_dec_and_test(&health->sp80090b_startup_blocks)) {
+ struct entropy_buf eb;
+
+ health->sp80090b_startup_done = true;
+ pr_info("SP800-90B startup health tests completed\n");
+ memset(&eb, 0, sizeof(eb));
+ lrng_init_ops(&eb);
+
+ /*
+ * Force a reseed of DRNGs to ensure they are seeded with
+ * entropy that passed the SP800-90B health tests.
+ * As the DRNG always will reseed before generating
+ * random numbers, it does not need a reseed trigger.
+ */
+ lrng_drng_force_reseed();
+ }
+}
+
+/*
+ * Handle failure of SP800-90B startup testing
+ */
+static inline void lrng_sp80090b_startup_failure(struct lrng_health *health)
+{
+ /* Reset of LRNG and its entropy - NOTE: we are in atomic context */
+ lrng_reset();
+
+ /*
+ * Reset the SP800-90B startup test.
+ *
+ * NOTE SP800-90B section 4.3 bullet 4 does not specify what
+ * exactly is to be done in case of failure! Thus, we do what
+ * makes sense, i.e. restarting the health test and thus gating
+ * the output function of /dev/random and getrandom(2).
+ */
+ atomic_set(&health->sp80090b_startup_blocks,
+ LRNG_SP80090B_STARTUP_BLOCKS);
+}
+
+/*
+ * Handle failure of SP800-90B runtime testing
+ */
+static inline void lrng_sp80090b_runtime_failure(struct lrng_health *health)
+{
+ lrng_sp80090b_startup_failure(health);
+ health->sp80090b_startup_done = false;
+}
+
+static inline void lrng_sp80090b_failure(struct lrng_health *health)
+{
+ if (health->sp80090b_startup_done) {
+ pr_err("SP800-90B runtime health test failure - invalidating all existing entropy and initiate SP800-90B startup\n");
+ lrng_sp80090b_runtime_failure(health);
+ } else {
+ pr_err("SP800-90B startup test failure - resetting\n");
+ lrng_sp80090b_startup_failure(health);
+ }
+}
+
+/*
+ * Is the SP800-90B startup testing complete?
+ *
+ * This function is called by the LRNG to determine whether to unblock
+ * a certain user interface. Therefore, only the potentially blocking
+ * user interfaces are considered SP800-90B compliant.
+ */
+bool lrng_sp80090b_startup_complete(void)
+{
+ struct lrng_health *health = &lrng_health;
+
+ return (lrng_sp80090b_health_enabled()) ? health->sp80090b_startup_done:
+ true;
+}
+
+bool lrng_sp80090b_compliant(void)
+{
+ struct lrng_health *health = &lrng_health;
+
+ return lrng_sp80090b_health_enabled() && health->sp80090b_startup_done;
+}
+
+/***************************************************************************
+ * Adaptive Proportion Test
+ *
+ * This test complies with SP800-90B section 4.4.2.
+ ***************************************************************************/
+
+/*
+ * Reset the APT counter
+ *
+ * @health [in] Reference to health state
+ */
+static inline void lrng_apt_reset(struct lrng_health *health,
+ unsigned int time_masked)
+{
+ struct lrng_apt *apt = &health->apt;
+
+ pr_debug("APT value %d for base %d\n",
+ atomic_read(&apt->apt_count), atomic_read(&apt->apt_base));
+
+ /* Reset APT */
+ atomic_set(&apt->apt_count, 0);
+ atomic_set(&apt->apt_base, time_masked);
+}
+
+static inline void lrng_apt_restart(struct lrng_health *health)
+{
+ struct lrng_apt *apt = &health->apt;
+
+ atomic_set(&apt->apt_trigger, LRNG_APT_WINDOW_SIZE);
+}
+
+/*
+ * Insert a new entropy event into APT
+ *
+ * This function does is void as it does not decide about the fate of a time
+ * stamp. An APT failure can only happen at the same time of a stuck test
+ * failure. Thus, the stuck failure will already decide how the time stamp
+ * is handled.
+ *
+ * @health [in] Reference to health state
+ * @now_time [in] Time stamp to process
+ */
+static inline void lrng_apt_insert(struct lrng_health *health,
+ unsigned int now_time)
+{
+ struct lrng_apt *apt = &health->apt;
+
+ if (!lrng_sp80090b_health_requested())
+ return;
+
+ now_time &= LRNG_APT_WORD_MASK;
+
+ /* Initialization of APT */
+ if (!apt->apt_base_set) {
+ atomic_set(&apt->apt_base, now_time);
+ apt->apt_base_set = true;
+ return;
+ }
+
+ if (now_time == (unsigned int)atomic_read(&apt->apt_base)) {
+ u32 apt_val = (u32)atomic_inc_return_relaxed(&apt->apt_count);
+
+ if (apt_val >= CONFIG_LRNG_APT_CUTOFF)
+ lrng_sp80090b_failure(health);
+ }
+
+ if (atomic_dec_and_test(&apt->apt_trigger)) {
+ lrng_apt_restart(health);
+ lrng_apt_reset(health, now_time);
+ lrng_sp80090b_startup(health);
+ }
+}
+
+/***************************************************************************
+ * Repetition Count Test
+ *
+ * The LRNG uses an enhanced version of the Repetition Count Test
+ * (RCT) specified in SP800-90B section 4.4.1. Instead of counting identical
+ * back-to-back values, the input to the RCT is the counting of the stuck
+ * values while filling the entropy pool.
+ *
+ * The RCT is applied with an alpha of 2^-30 compliant to FIPS 140-2 IG 9.8.
+ *
+ * During the counting operation, the LRNG always calculates the RCT
+ * cut-off value of C. If that value exceeds the allowed cut-off value,
+ * the LRNG will invalidate all entropy for the entropy pool which implies
+ * that no data can be extracted from the entropy pool unless new entropy
+ * is received.
+ ***************************************************************************/
+
+/*
+ * Hot code path - Insert data for Repetition Count Test
+ *
+ * @health: Reference to health information
+ * @stuck: Decision of stuck test
+ */
+static inline void lrng_rct(struct lrng_health *health, int stuck)
+{
+ struct lrng_rct *rct = &health->rct;
+
+ if (!lrng_sp80090b_health_requested())
+ return;
+
+ if (stuck) {
+ u32 rct_count = atomic_add_return_relaxed(1, &rct->rct_count);
+
+ pr_debug("RCT count: %u\n", rct_count);
+
+ /*
+ * The cutoff value is based on the following consideration:
+ * alpha = 2^-30 as recommended in FIPS 140-2 IG 9.8.
+ * In addition, we imply an entropy value H of 1 bit as this
+ * is the minimum entropy required to provide full entropy.
+ *
+ * Note, rct_count (which equals to value B in the
+ * pseudo code of SP800-90B section 4.4.1) starts with zero.
+ * Hence we need to subtract one from the cutoff value as
+ * calculated following SP800-90B.
+ */
+ if (rct_count >= CONFIG_LRNG_RCT_CUTOFF) {
+ atomic_set(&rct->rct_count, 0);
+
+ /*
+ * APT must start anew as we consider all previously
+ * recorded data to contain no entropy.
+ */
+ lrng_apt_restart(health);
+
+ lrng_sp80090b_failure(health);
+ }
+ } else {
+ atomic_set(&rct->rct_count, 0);
+ }
+}
+
+/***************************************************************************
+ * Stuck Test
+ *
+ * Checking the:
+ * 1st derivative of the event occurrence (time delta)
+ * 2nd derivative of the event occurrence (delta of time deltas)
+ * 3rd derivative of the event occurrence (delta of delta of time deltas)
+ *
+ * All values must always be non-zero. The stuck test is only valid disabled if
+ * high-resolution time stamps are identified after initialization.
+ ***************************************************************************/
+
+static inline u32 lrng_delta(u32 prev, u32 next)
+{
+ /*
+ * Note that this (unsigned) subtraction does yield the correct value
+ * in the wraparound-case, i.e. when next < prev.
+ */
+ return (next - prev);
+}
+
+/*
+ * Hot code path
+ *
+ * @health: Reference to health information
+ * @now: Event time
+ * @return: 0 event occurrence not stuck (good time stamp)
+ * != 0 event occurrence stuck (reject time stamp)
+ */
+static inline int lrng_irq_stuck(struct lrng_stuck_test *stuck, u32 now_time)
+{
+ u32 delta = lrng_delta(stuck->last_time, now_time);
+ u32 delta2 = lrng_delta(stuck->last_delta, delta);
+ u32 delta3 = lrng_delta(stuck->last_delta2, delta2);
+
+ stuck->last_time = now_time;
+ stuck->last_delta = delta;
+ stuck->last_delta2 = delta2;
+
+ if (!delta || !delta2 || !delta3)
+ return 1;
+
+ return 0;
+}
+
+/***************************************************************************
+ * Health test interfaces
+ ***************************************************************************/
+
+/*
+ * Disable all health tests
+ */
+void lrng_health_disable(void)
+{
+ struct lrng_health *health = &lrng_health;
+
+ health->health_test_enabled = false;
+
+ if (lrng_sp80090b_health_requested())
+ pr_warn("SP800-90B compliance requested but the Linux RNG is NOT SP800-90B compliant\n");
+}
+
+/*
+ * Hot code path - Perform health test on time stamp received from an event
+ *
+ * @now_time Time stamp
+ */
+enum lrng_health_res lrng_health_test(u32 now_time)
+{
+ struct lrng_health *health = &lrng_health;
+ struct lrng_stuck_test *stuck_test = this_cpu_ptr(&lrng_stuck_test);
+ int stuck;
+
+ if (!health->health_test_enabled)
+ return lrng_health_pass;
+
+ lrng_apt_insert(health, now_time);
+
+ stuck = lrng_irq_stuck(stuck_test, now_time);
+ lrng_rct(health, stuck);
+ if (stuck) {
+ /* SP800-90B disallows using a failing health test time stamp */
+ return lrng_sp80090b_health_requested() ?
+ lrng_health_fail_drop : lrng_health_fail_use;
+ }
+
+ return lrng_health_pass;
+}
--
2.31.1




2021-07-14 05:52:40

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v41 05/13] LRNG - add common generic hash support

The LRNG switchable DRNG support also allows the replacement of the hash
implementation used as conditioning component. The common generic hash
support code provides the required callbacks using the synchronous hash
implementations of the kernel crypto API.

All synchronous hash implementations supported by the kernel crypto API
can be used as part of the LRNG with this generic support.

The generic support is intended to be configured by separate switchable
DRNG backends.

CC: Torsten Duwe <[email protected]>
CC: "Eric W. Biederman" <[email protected]>
CC: "Alexander E. Patrakov" <[email protected]>
CC: "Ahmed S. Darwish" <[email protected]>
CC: "Theodore Y. Ts'o" <[email protected]>
CC: Willy Tarreau <[email protected]>
CC: Matthew Garrett <[email protected]>
CC: Vito Caputo <[email protected]>
CC: Andreas Dilger <[email protected]>
CC: Jan Kara <[email protected]>
CC: Ray Strode <[email protected]>
CC: William Jon McCann <[email protected]>
CC: zhangjs <[email protected]>
CC: Andy Lutomirski <[email protected]>
CC: Florian Weimer <[email protected]>
CC: Lennart Poettering <[email protected]>
CC: Nicolai Stange <[email protected]>
CC: Alexander Lobakin <[email protected]>
CC: "Peter, Matthias" <[email protected]>
CC: Marcelo Henrique Cerri <[email protected]>
CC: Neil Horman <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Kconfig | 8 +++
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_kcapi_hash.c | 103 ++++++++++++++++++++++++++++
drivers/char/lrng/lrng_kcapi_hash.h | 20 ++++++
4 files changed, 132 insertions(+)
create mode 100644 drivers/char/lrng/lrng_kcapi_hash.c
create mode 100644 drivers/char/lrng/lrng_kcapi_hash.h

diff --git a/drivers/char/lrng/Kconfig b/drivers/char/lrng/Kconfig
index c10a0c3f2015..ab17c45a356b 100644
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -209,4 +209,12 @@ menuconfig LRNG_DRNG_SWITCH
accessible via the external interfaces. With this configuration
option other DRNGs can be selected and loaded at runtime.

+if LRNG_DRNG_SWITCH
+
+config LRNG_KCAPI_HASH
+ bool
+ select CRYPTO_HASH
+
+endif # LRNG_DRNG_SWITCH
+
endif # LRNG
diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile
index 0eb4a6849c88..40f8826edeeb 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -11,3 +11,4 @@ obj-y += lrng_pool.o lrng_aux.o \
obj-$(CONFIG_NUMA) += lrng_numa.o
obj-$(CONFIG_SYSCTL) += lrng_proc.o
obj-$(CONFIG_LRNG_DRNG_SWITCH) += lrng_switch.o
+obj-$(CONFIG_LRNG_KCAPI_HASH) += lrng_kcapi_hash.o
diff --git a/drivers/char/lrng/lrng_kcapi_hash.c b/drivers/char/lrng/lrng_kcapi_hash.c
new file mode 100644
index 000000000000..9647d980e468
--- /dev/null
+++ b/drivers/char/lrng/lrng_kcapi_hash.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Backend for providing the hash primitive using the kernel crypto API.
+ *
+ * Copyright (C) 2021, Stephan Mueller <[email protected]>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <crypto/hash.h>
+
+#include "lrng_kcapi_hash.h"
+
+struct lrng_hash_info {
+ struct crypto_shash *tfm;
+};
+
+static inline void _lrng_kcapi_hash_free(struct lrng_hash_info *lrng_hash)
+{
+ struct crypto_shash *tfm = lrng_hash->tfm;
+
+ crypto_free_shash(tfm);
+ kfree(lrng_hash);
+}
+
+void *lrng_kcapi_hash_alloc(const char *name)
+{
+ struct lrng_hash_info *lrng_hash;
+ struct crypto_shash *tfm;
+ int ret;
+
+ if (!name) {
+ pr_err("Hash name missing\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ tfm = crypto_alloc_shash(name, 0, 0);
+ if (IS_ERR(tfm)) {
+ pr_err("could not allocate hash %s\n", name);
+ return ERR_CAST(tfm);
+ }
+
+ ret = sizeof(struct lrng_hash_info);
+ lrng_hash = kmalloc(ret, GFP_KERNEL);
+ if (!lrng_hash) {
+ crypto_free_shash(tfm);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ lrng_hash->tfm = tfm;
+
+ pr_info("Hash %s allocated\n", name);
+
+ return lrng_hash;
+}
+EXPORT_SYMBOL(lrng_kcapi_hash_alloc);
+
+u32 lrng_kcapi_hash_digestsize(void *hash)
+{
+ struct lrng_hash_info *lrng_hash = (struct lrng_hash_info *)hash;
+ struct crypto_shash *tfm = lrng_hash->tfm;
+
+ return crypto_shash_digestsize(tfm);
+}
+EXPORT_SYMBOL(lrng_kcapi_hash_digestsize);
+
+void lrng_kcapi_hash_dealloc(void *hash)
+{
+ struct lrng_hash_info *lrng_hash = (struct lrng_hash_info *)hash;
+
+ _lrng_kcapi_hash_free(lrng_hash);
+ pr_info("Hash deallocated\n");
+}
+EXPORT_SYMBOL(lrng_kcapi_hash_dealloc);
+
+int lrng_kcapi_hash_init(struct shash_desc *shash, void *hash)
+{
+ struct lrng_hash_info *lrng_hash = (struct lrng_hash_info *)hash;
+ struct crypto_shash *tfm = lrng_hash->tfm;
+
+ shash->tfm = tfm;
+ return crypto_shash_init(shash);
+}
+EXPORT_SYMBOL(lrng_kcapi_hash_init);
+
+int lrng_kcapi_hash_update(struct shash_desc *shash, const u8 *inbuf,
+ u32 inbuflen)
+{
+ return crypto_shash_update(shash, inbuf, inbuflen);
+}
+EXPORT_SYMBOL(lrng_kcapi_hash_update);
+
+int lrng_kcapi_hash_final(struct shash_desc *shash, u8 *digest)
+{
+ return crypto_shash_final(shash, digest);
+}
+EXPORT_SYMBOL(lrng_kcapi_hash_final);
+
+void lrng_kcapi_hash_zero(struct shash_desc *shash)
+{
+ shash_desc_zero(shash);
+}
+EXPORT_SYMBOL(lrng_kcapi_hash_zero);
diff --git a/drivers/char/lrng/lrng_kcapi_hash.h b/drivers/char/lrng/lrng_kcapi_hash.h
new file mode 100644
index 000000000000..2f94558d2dd6
--- /dev/null
+++ b/drivers/char/lrng/lrng_kcapi_hash.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2020 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#ifndef _LRNG_KCAPI_HASH_H
+#define _LRNG_KCAPI_HASH_H
+
+#include <linux/module.h>
+
+void *lrng_kcapi_hash_alloc(const char *name);
+u32 lrng_kcapi_hash_digestsize(void *hash);
+void lrng_kcapi_hash_dealloc(void *hash);
+int lrng_kcapi_hash_init(struct shash_desc *shash, void *hash);
+int lrng_kcapi_hash_update(struct shash_desc *shash, const u8 *inbuf,
+ u32 inbuflen);
+int lrng_kcapi_hash_final(struct shash_desc *shash, u8 *digest);
+void lrng_kcapi_hash_zero(struct shash_desc *shash);
+
+#endif /* _LRNG_KCAPI_HASH_H */
--
2.31.1




2021-07-14 05:52:41

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v41 06/13] crypto: DRBG - externalize DRBG functions for LRNG

This patch allows several DRBG functions to be called by the LRNG kernel
code paths outside the drbg.c file.

CC: Torsten Duwe <[email protected]>
CC: "Eric W. Biederman" <[email protected]>
CC: "Alexander E. Patrakov" <[email protected]>
CC: "Ahmed S. Darwish" <[email protected]>
CC: "Theodore Y. Ts'o" <[email protected]>
CC: Willy Tarreau <[email protected]>
CC: Matthew Garrett <[email protected]>
CC: Vito Caputo <[email protected]>
CC: Andreas Dilger <[email protected]>
CC: Jan Kara <[email protected]>
CC: Ray Strode <[email protected]>
CC: William Jon McCann <[email protected]>
CC: zhangjs <[email protected]>
CC: Andy Lutomirski <[email protected]>
CC: Florian Weimer <[email protected]>
CC: Lennart Poettering <[email protected]>
CC: Nicolai Stange <[email protected]>
CC: Alexander Lobakin <[email protected]>
Reviewed-by: Roman Drahtmueller <[email protected]>
Tested-by: Roman Drahtm?ller <[email protected]>
Tested-by: Marcelo Henrique Cerri <[email protected]>
Tested-by: Neil Horman <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
crypto/drbg.c | 16 ++++++++++------
include/crypto/drbg.h | 7 +++++++
2 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/crypto/drbg.c b/crypto/drbg.c
index 1b4587e0ddad..b7c962a882c4 100644
--- a/crypto/drbg.c
+++ b/crypto/drbg.c
@@ -114,7 +114,7 @@
* the SHA256 / AES 256 over other ciphers. Thus, the favored
* DRBGs are the latest entries in this array.
*/
-static const struct drbg_core drbg_cores[] = {
+const struct drbg_core drbg_cores[] = {
#ifdef CONFIG_CRYPTO_DRBG_CTR
{
.flags = DRBG_CTR | DRBG_STRENGTH128,
@@ -191,6 +191,7 @@ static const struct drbg_core drbg_cores[] = {
},
#endif /* CONFIG_CRYPTO_DRBG_HMAC */
};
+EXPORT_SYMBOL(drbg_cores);

static int drbg_uninstantiate(struct drbg_state *drbg);

@@ -206,7 +207,7 @@ static int drbg_uninstantiate(struct drbg_state *drbg);
* Return: normalized strength in *bytes* value or 32 as default
* to counter programming errors
*/
-static inline unsigned short drbg_sec_strength(drbg_flag_t flags)
+unsigned short drbg_sec_strength(drbg_flag_t flags)
{
switch (flags & DRBG_STRENGTH_MASK) {
case DRBG_STRENGTH128:
@@ -219,6 +220,7 @@ static inline unsigned short drbg_sec_strength(drbg_flag_t flags)
return 32;
}
}
+EXPORT_SYMBOL(drbg_sec_strength);

/*
* FIPS 140-2 continuous self test for the noise source
@@ -1215,7 +1217,7 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
}

/* Free all substructures in a DRBG state without the DRBG state structure */
-static inline void drbg_dealloc_state(struct drbg_state *drbg)
+void drbg_dealloc_state(struct drbg_state *drbg)
{
if (!drbg)
return;
@@ -1236,12 +1238,13 @@ static inline void drbg_dealloc_state(struct drbg_state *drbg)
drbg->fips_primed = false;
}
}
+EXPORT_SYMBOL(drbg_dealloc_state);

/*
* Allocate all sub-structures for a DRBG state.
* The DRBG state structure must already be allocated.
*/
-static inline int drbg_alloc_state(struct drbg_state *drbg)
+int drbg_alloc_state(struct drbg_state *drbg)
{
int ret = -ENOMEM;
unsigned int sb_size = 0;
@@ -1322,6 +1325,7 @@ static inline int drbg_alloc_state(struct drbg_state *drbg)
drbg_dealloc_state(drbg);
return ret;
}
+EXPORT_SYMBOL(drbg_alloc_state);

/*************************************************************************
* DRBG interface functions
@@ -1891,8 +1895,7 @@ static int drbg_kcapi_sym_ctr(struct drbg_state *drbg,
*
* return: flags
*/
-static inline void drbg_convert_tfm_core(const char *cra_driver_name,
- int *coreref, bool *pr)
+void drbg_convert_tfm_core(const char *cra_driver_name, int *coreref, bool *pr)
{
int i = 0;
size_t start = 0;
@@ -1919,6 +1922,7 @@ static inline void drbg_convert_tfm_core(const char *cra_driver_name,
}
}
}
+EXPORT_SYMBOL(drbg_convert_tfm_core);

static int drbg_kcapi_init(struct crypto_tfm *tfm)
{
diff --git a/include/crypto/drbg.h b/include/crypto/drbg.h
index c4165126937e..71d53e028e6d 100644
--- a/include/crypto/drbg.h
+++ b/include/crypto/drbg.h
@@ -278,4 +278,11 @@ enum drbg_prefixes {
DRBG_PREFIX3
};

+extern int drbg_alloc_state(struct drbg_state *drbg);
+extern void drbg_dealloc_state(struct drbg_state *drbg);
+extern void drbg_convert_tfm_core(const char *cra_driver_name, int *coreref,
+ bool *pr);
+extern const struct drbg_core drbg_cores[];
+extern unsigned short drbg_sec_strength(drbg_flag_t flags);
+
#endif /* _DRBG_H */
--
2.31.1




2021-07-14 05:52:45

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v41 08/13] LRNG - add kernel crypto API PRNG extension

Add runtime-pluggable support for all PRNGs that are accessible via
the kernel crypto API, including hardware PRNGs. The PRNG is selected
with the module parameter drng_name where the name must be one that the
kernel crypto API can resolve into an RNG.

This allows using of the kernel crypto API PRNG implementations that
provide an interface to hardware PRNGs. Using this extension,
the LRNG uses the hardware PRNGs to generate random numbers. An
example is the S390 CPACF support providing such a PRNG.

The hash is provided by a kernel crypto API SHASH whose digest size
complies with the seedsize of the PRNG.

CC: Torsten Duwe <[email protected]>
CC: "Eric W. Biederman" <[email protected]>
CC: "Alexander E. Patrakov" <[email protected]>
CC: "Ahmed S. Darwish" <[email protected]>
CC: "Theodore Y. Ts'o" <[email protected]>
CC: Willy Tarreau <[email protected]>
CC: Matthew Garrett <[email protected]>
CC: Vito Caputo <[email protected]>
CC: Andreas Dilger <[email protected]>
CC: Jan Kara <[email protected]>
CC: Ray Strode <[email protected]>
CC: William Jon McCann <[email protected]>
CC: zhangjs <[email protected]>
CC: Andy Lutomirski <[email protected]>
CC: Florian Weimer <[email protected]>
CC: Lennart Poettering <[email protected]>
CC: Nicolai Stange <[email protected]>
CC: Alexander Lobakin <[email protected]>
Reviewed-by: Marcelo Henrique Cerri <[email protected]>
Reviewed-by: Roman Drahtmueller <[email protected]>
Tested-by: Marcelo Henrique Cerri <[email protected]>
Tested-by: Neil Horman <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Kconfig | 13 ++
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_kcapi.c | 227 +++++++++++++++++++++++++++++++++
3 files changed, 241 insertions(+)
create mode 100644 drivers/char/lrng/lrng_kcapi.c

diff --git a/drivers/char/lrng/Kconfig b/drivers/char/lrng/Kconfig
index aa611363b97e..ffd2df43f2d4 100644
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -225,6 +225,19 @@ config LRNG_DRBG
Enable the SP800-90A DRBG support for the LRNG. Once the
module is loaded, output from /dev/random, /dev/urandom,
getrandom(2), or get_random_bytes_full is provided by a DRBG.
+
+config LRNG_KCAPI
+ tristate "Kernel Crypto API support for the LRNG"
+ depends on CRYPTO
+ depends on !LRNG_DRBG
+ select CRYPTO_RNG
+ select LRNG_KCAPI_HASH
+ help
+ Enable the support for generic pseudo-random number
+ generators offered by the kernel crypto API with the
+ LRNG. Once the module is loaded, output from /dev/random,
+ /dev/urandom, getrandom(2), or get_random_bytes is
+ provided by the selected kernel crypto API RNG.
endif # LRNG_DRNG_SWITCH

endif # LRNG
diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile
index 6ebd252db12f..97d2b13d3227 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_SYSCTL) += lrng_proc.o
obj-$(CONFIG_LRNG_DRNG_SWITCH) += lrng_switch.o
obj-$(CONFIG_LRNG_KCAPI_HASH) += lrng_kcapi_hash.o
obj-$(CONFIG_LRNG_DRBG) += lrng_drbg.o
+obj-$(CONFIG_LRNG_KCAPI) += lrng_kcapi.o
diff --git a/drivers/char/lrng/lrng_kcapi.c b/drivers/char/lrng/lrng_kcapi.c
new file mode 100644
index 000000000000..b06449cb2365
--- /dev/null
+++ b/drivers/char/lrng/lrng_kcapi.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Backend for the LRNG providing the cryptographic primitives using the
+ * kernel crypto API.
+ *
+ * Copyright (C) 2018 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <crypto/hash.h>
+#include <crypto/rng.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/lrng.h>
+
+#include "lrng_kcapi_hash.h"
+
+static char *drng_name = NULL;
+module_param(drng_name, charp, 0444);
+MODULE_PARM_DESC(drng_name, "Kernel crypto API name of DRNG");
+
+static char *pool_hash = "sha512";
+module_param(pool_hash, charp, 0444);
+MODULE_PARM_DESC(pool_hash,
+ "Kernel crypto API name of hash or keyed message digest to read the entropy pool");
+
+static char *seed_hash = NULL;
+module_param(seed_hash, charp, 0444);
+MODULE_PARM_DESC(seed_hash,
+ "Kernel crypto API name of hash with output size equal to seedsize of DRNG to bring seed string to the size required by the DRNG");
+
+struct lrng_drng_info {
+ struct crypto_rng *kcapi_rng;
+ void *lrng_hash;
+};
+
+static void *lrng_kcapi_drng_hash_alloc(void)
+{
+ return lrng_kcapi_hash_alloc(pool_hash);
+}
+
+static int lrng_kcapi_drng_seed_helper(void *drng, const u8 *inbuf,
+ u32 inbuflen)
+{
+ SHASH_DESC_ON_STACK(shash, NULL);
+ struct lrng_drng_info *lrng_drng_info = (struct lrng_drng_info *)drng;
+ struct crypto_rng *kcapi_rng = lrng_drng_info->kcapi_rng;
+ void *hash = lrng_drng_info->lrng_hash;
+ u32 digestsize = lrng_kcapi_hash_digestsize(hash);
+ u8 digest[64] __aligned(8);
+ int ret;
+
+ if (!hash)
+ return crypto_rng_reset(kcapi_rng, inbuf, inbuflen);
+
+ BUG_ON(digestsize > sizeof(digest));
+
+ ret = lrng_kcapi_hash_init(shash, hash) ?:
+ lrng_kcapi_hash_update(shash, inbuf, inbuflen) ?:
+ lrng_kcapi_hash_final(shash, digest);
+ lrng_kcapi_hash_zero(shash);
+ if (ret)
+ return ret;
+
+ ret = crypto_rng_reset(kcapi_rng, digest, digestsize);
+ if (ret)
+ return ret;
+
+ memzero_explicit(digest, digestsize);
+ return 0;
+}
+
+static int lrng_kcapi_drng_generate_helper(void *drng, u8 *outbuf,
+ u32 outbuflen)
+{
+ struct lrng_drng_info *lrng_drng_info = (struct lrng_drng_info *)drng;
+ struct crypto_rng *kcapi_rng = lrng_drng_info->kcapi_rng;
+ int ret = crypto_rng_get_bytes(kcapi_rng, outbuf, outbuflen);
+
+ if (ret < 0)
+ return ret;
+
+ return outbuflen;
+}
+
+static void *lrng_kcapi_drng_alloc(u32 sec_strength)
+{
+ struct lrng_drng_info *lrng_drng_info;
+ struct crypto_rng *kcapi_rng;
+ int seedsize;
+ void *ret = ERR_PTR(-ENOMEM);
+
+ if (!drng_name) {
+ pr_err("DRNG name missing\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!memcmp(drng_name, "drbg", 4) ||
+ !memcmp(drng_name, "stdrng", 6) ||
+ !memcmp(drng_name, "jitterentropy_rng", 17)) {
+ pr_err("Refusing to load the requested random number generator\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ lrng_drng_info = kmalloc(sizeof(*lrng_drng_info), GFP_KERNEL);
+ if (!lrng_drng_info)
+ return ERR_PTR(-ENOMEM);
+
+ kcapi_rng = crypto_alloc_rng(drng_name, 0, 0);
+ if (IS_ERR(kcapi_rng)) {
+ pr_err("DRNG %s cannot be allocated\n", drng_name);
+ ret = ERR_CAST(kcapi_rng);
+ goto free;
+ }
+ lrng_drng_info->kcapi_rng = kcapi_rng;
+
+ seedsize = crypto_rng_seedsize(kcapi_rng);
+
+ if (sec_strength > seedsize)
+ pr_info("Seedsize DRNG (%u bits) lower than security strength of LRNG noise source (%u bits)\n",
+ crypto_rng_seedsize(kcapi_rng) * 8, sec_strength * 8);
+
+ if (seedsize) {
+ void *lrng_hash;
+
+ if (!seed_hash) {
+ switch (seedsize) {
+ case 32:
+ seed_hash = "sha256";
+ break;
+ case 48:
+ seed_hash = "sha384";
+ break;
+ case 64:
+ seed_hash = "sha512";
+ break;
+ default:
+ pr_err("Seed size %d cannot be processed\n",
+ seedsize);
+ goto dealloc;
+ }
+ }
+
+ lrng_hash = lrng_kcapi_hash_alloc(seed_hash);
+ if (IS_ERR(lrng_hash)) {
+ ret = ERR_CAST(lrng_hash);
+ goto dealloc;
+ }
+
+ if (seedsize != lrng_kcapi_hash_digestsize(lrng_hash)) {
+ pr_err("Seed hash output size not equal to DRNG seed size\n");
+ lrng_kcapi_hash_dealloc(lrng_hash);
+ ret = ERR_PTR(-EINVAL);
+ goto dealloc;
+ }
+
+ lrng_drng_info->lrng_hash = lrng_hash;
+
+ pr_info("Seed hash %s allocated\n", seed_hash);
+ } else {
+ lrng_drng_info->lrng_hash = NULL;
+ }
+
+ pr_info("Kernel crypto API DRNG %s allocated\n", drng_name);
+
+ return lrng_drng_info;
+
+dealloc:
+ crypto_free_rng(kcapi_rng);
+free:
+ kfree(lrng_drng_info);
+ return ret;
+}
+
+static void lrng_kcapi_drng_dealloc(void *drng)
+{
+ struct lrng_drng_info *lrng_drng_info = (struct lrng_drng_info *)drng;
+ struct crypto_rng *kcapi_rng = lrng_drng_info->kcapi_rng;
+
+ crypto_free_rng(kcapi_rng);
+ if (lrng_drng_info->lrng_hash)
+ lrng_kcapi_hash_dealloc(lrng_drng_info->lrng_hash);
+ kfree(lrng_drng_info);
+ pr_info("DRNG %s deallocated\n", drng_name);
+}
+
+static const char *lrng_kcapi_drng_name(void)
+{
+ return drng_name;
+}
+
+static const char *lrng_kcapi_pool_hash(void)
+{
+ return pool_hash;
+}
+
+static const struct lrng_crypto_cb lrng_kcapi_crypto_cb = {
+ .lrng_drng_name = lrng_kcapi_drng_name,
+ .lrng_hash_name = lrng_kcapi_pool_hash,
+ .lrng_drng_alloc = lrng_kcapi_drng_alloc,
+ .lrng_drng_dealloc = lrng_kcapi_drng_dealloc,
+ .lrng_drng_seed_helper = lrng_kcapi_drng_seed_helper,
+ .lrng_drng_generate_helper = lrng_kcapi_drng_generate_helper,
+ .lrng_hash_alloc = lrng_kcapi_drng_hash_alloc,
+ .lrng_hash_dealloc = lrng_kcapi_hash_dealloc,
+ .lrng_hash_digestsize = lrng_kcapi_hash_digestsize,
+ .lrng_hash_init = lrng_kcapi_hash_init,
+ .lrng_hash_update = lrng_kcapi_hash_update,
+ .lrng_hash_final = lrng_kcapi_hash_final,
+ .lrng_hash_desc_zero = lrng_kcapi_hash_zero,
+};
+
+static int __init lrng_kcapi_init(void)
+{
+ return lrng_set_drng_cb(&lrng_kcapi_crypto_cb);
+}
+static void __exit lrng_kcapi_exit(void)
+{
+ lrng_set_drng_cb(NULL);
+}
+
+late_initcall(lrng_kcapi_init);
+module_exit(lrng_kcapi_exit);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Stephan Mueller <[email protected]>");
+MODULE_DESCRIPTION("Linux Random Number Generator - kernel crypto API DRNG backend");
--
2.31.1




2021-07-14 05:52:49

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v41 13/13] LRNG - add power-on and runtime self-tests

Parts of the LRNG are already covered by self-tests, including:

* Self-test of SP800-90A DRBG provided by the Linux kernel crypto API.

* Self-test of the PRNG provided by the Linux kernel crypto API.

* Raw noise source data testing including SP800-90B compliant
tests when enabling CONFIG_LRNG_HEALTH_TESTS

This patch adds the self-tests for the remaining critical functions of
the LRNG that are essential to maintain entropy and provide
cryptographic strong random numbers. The following self-tests are
implemented:

* Self-test of the time array maintenance. This test verifies whether
the time stamp array management to store multiple values in one integer
implements a concatenation of the data.

* Self-test of the software hash implementation ensures that this
function operates compliant to the FIPS 180-4 specification. The
self-test performs a hash operation of a zeroized per-CPU data array.

* Self-test of the ChaCha20 DRNG is based on the self-tests that are
already present and implemented with the stand-alone user space
ChaCha20 DRNG implementation available at [1]. The self-tests cover
different use cases of the DRNG seeded with known seed data.

The status of the LRNG self-tests is provided with the selftest_status
SysFS file. If the file contains a zero, the self-tests passed. The
value 0xffffffff means that the self-tests were not executed. Any other
value indicates a self-test failure.

The self-test may be compiled to panic the system if the self-test
fails.

All self-tests operate on private state data structures. This implies
that none of the self-tests have any impact on the regular LRNG
operations. This allows the self-tests to be repeated at runtime by
writing anything into the selftest_status SysFS file.

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

CC: Torsten Duwe <[email protected]>
CC: "Eric W. Biederman" <[email protected]>
CC: "Alexander E. Patrakov" <[email protected]>
CC: "Ahmed S. Darwish" <[email protected]>
CC: "Theodore Y. Ts'o" <[email protected]>
CC: Willy Tarreau <[email protected]>
CC: Matthew Garrett <[email protected]>
CC: Vito Caputo <[email protected]>
CC: Andreas Dilger <[email protected]>
CC: Jan Kara <[email protected]>
CC: Ray Strode <[email protected]>
CC: William Jon McCann <[email protected]>
CC: zhangjs <[email protected]>
CC: Andy Lutomirski <[email protected]>
CC: Florian Weimer <[email protected]>
CC: Lennart Poettering <[email protected]>
CC: Nicolai Stange <[email protected]>
CC: Marcelo Henrique Cerri <[email protected]>
CC: Neil Horman <[email protected]>
CC: Alexander Lobakin <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Kconfig | 43 +++-
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_selftest.c | 351 ++++++++++++++++++++++++++++++
3 files changed, 387 insertions(+), 8 deletions(-)
create mode 100644 drivers/char/lrng/lrng_selftest.c

diff --git a/drivers/char/lrng/Kconfig b/drivers/char/lrng/Kconfig
index 379e89e7fa53..0c654f4db5e1 100644
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -165,15 +165,16 @@ config LRNG_COLLECTION_SIZE
default 8192 if LRNG_COLLECTION_SIZE_8192

config LRNG_HEALTH_TESTS
- bool "Enable noise source online health tests"
+ bool "Enable interrupt entropy source online health tests"
help
- The online health tests validate the noise source at
- runtime for fatal errors. These tests include SP800-90B
- compliant tests which are invoked if the system is booted
- with fips=1. In case of fatal errors during active
- SP800-90B tests, the issue is logged and the noise
- data is discarded. These tests are required for full
- compliance with SP800-90B.
+ The online health tests applied to the interrupt entropy
+ source validate the noise source at runtime for fatal
+ errors. These tests include SP800-90B compliant tests
+ which are invoked if the system is booted with fips=1.
+ In case of fatal errors during active SP800-90B tests,
+ the issue is logged and the noise data is discarded.
+ These tests are required for full compliance of the
+ interrupt entropy source with SP800-90B.

If unsure, say Y.

@@ -485,4 +486,30 @@ config LRNG_TESTING

endif #LRNG_TESTING_MENU

+config LRNG_SELFTEST
+ bool "Enable power-on and on-demand self-tests"
+ help
+ The power-on self-tests are executed during boot time
+ covering the ChaCha20 DRNG, the hash operation used for
+ processing the entropy pools and the auxiliary pool, and
+ the time stamp management of the LRNG.
+
+ The on-demand self-tests are triggered by writing any
+ value into the SysFS file selftest_status. At the same
+ time, when reading this file, the test status is
+ returned. A zero indicates that all tests were executed
+ successfully.
+
+ If unsure, say Y.
+
+if LRNG_SELFTEST
+
+config LRNG_SELFTEST_PANIC
+ bool "Panic the kernel upon self-test failure"
+ help
+ If the option is enabled, the kernel is terminated if an
+ LRNG power-on self-test failure is detected.
+
+endif # LRNG_SELFTEST
+
endif # LRNG
diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile
index 532501b38a00..a633638af991 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_LRNG_KCAPI) += lrng_kcapi.o
obj-$(CONFIG_LRNG_JENT) += lrng_jent.o
obj-$(CONFIG_LRNG_HEALTH_TESTS) += lrng_health.o
obj-$(CONFIG_LRNG_TESTING) += lrng_testing.o
+obj-$(CONFIG_LRNG_SELFTEST) += lrng_selftest.o
diff --git a/drivers/char/lrng/lrng_selftest.c b/drivers/char/lrng/lrng_selftest.c
new file mode 100644
index 000000000000..cd2b64184f01
--- /dev/null
+++ b/drivers/char/lrng/lrng_selftest.c
@@ -0,0 +1,351 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG power-on and on-demand self-test
+ *
+ * Copyright (C) 2016 - 2021, Stephan Mueller <[email protected]>
+ */
+
+/*
+ * In addition to the self-tests below, the following LRNG components
+ * are covered with self-tests during regular operation:
+ *
+ * * power-on self-test: SP800-90A DRBG provided by the Linux kernel crypto API
+ * * power-on self-test: PRNG provided by the Linux kernel crypto API
+ * * runtime test: Raw noise source data testing including SP800-90B compliant
+ * tests when enabling CONFIG_LRNG_HEALTH_TESTS
+ *
+ * Additional developer tests present with LRNG code:
+ * * SP800-90B APT and RCT test enforcement validation when enabling
+ * CONFIG_LRNG_APT_BROKEN or CONFIG_LRNG_RCT_BROKEN.
+ * * Collection of raw entropy from the interrupt noise source when enabling
+ * CONFIG_LRNG_TESTING and pulling the data from the kernel with the provided
+ * interface.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/lrng.h>
+#include <linux/slab.h>
+
+#include "lrng_chacha20.h"
+#include "lrng_internal.h"
+#include "lrng_sw_noise.h"
+
+#define LRNG_SELFTEST_PASSED 0
+#define LRNG_SEFLTEST_ERROR_TIME (1 << 0)
+#define LRNG_SEFLTEST_ERROR_CHACHA20 (1 << 1)
+#define LRNG_SEFLTEST_ERROR_HASH (1 << 2)
+#define LRNG_SELFTEST_NOT_EXECUTED 0xffffffff
+
+static u32 lrng_data_selftest_ptr = 0;
+static u32 lrng_data_selftest[LRNG_DATA_ARRAY_SIZE];
+
+static unsigned int lrng_selftest_status = LRNG_SELFTEST_NOT_EXECUTED;
+
+static inline void lrng_selftest_bswap32(u32 *ptr, u32 words)
+{
+ u32 i;
+
+ /* Byte-swap data which is an LE representation */
+ for (i = 0; i < words; i++) {
+ __le32 *p = (__le32 *)ptr;
+
+ *p = cpu_to_le32(*ptr);
+ ptr++;
+ }
+}
+
+static inline void lrng_data_process_selftest_insert(u32 time)
+{
+ u32 ptr = lrng_data_selftest_ptr++ & LRNG_DATA_WORD_MASK;
+ unsigned int array = lrng_data_idx2array(ptr);
+ unsigned int slot = lrng_data_idx2slot(ptr);
+
+ /* zeroization of slot to ensure the following OR adds the data */
+ lrng_data_selftest[array] &=
+ ~(lrng_data_slot_val(0xffffffff & LRNG_DATA_SLOTSIZE_MASK,
+ slot));
+ lrng_data_selftest[array] |=
+ lrng_data_slot_val(time & LRNG_DATA_SLOTSIZE_MASK, slot);
+}
+
+static inline void lrng_data_process_selftest_u32(u32 data)
+{
+ u32 pre_ptr, ptr, mask;
+ unsigned int pre_array;
+
+ /* Increment pointer by number of slots taken for input value */
+ lrng_data_selftest_ptr += LRNG_DATA_SLOTS_PER_UINT;
+
+ /* ptr to current unit */
+ ptr = lrng_data_selftest_ptr;
+
+ lrng_pcpu_split_u32(&ptr, &pre_ptr, &mask);
+
+ /* MSB of data go into previous unit */
+ pre_array = lrng_data_idx2array(pre_ptr);
+ /* zeroization of slot to ensure the following OR adds the data */
+ lrng_data_selftest[pre_array] &= ~(0xffffffff &~ mask);
+ lrng_data_selftest[pre_array] |= data & ~mask;
+
+ /* LSB of data go into current unit */
+ lrng_data_selftest[lrng_data_idx2array(ptr)] = data & mask;
+}
+
+static unsigned int lrng_data_process_selftest(void)
+{
+ u32 time;
+ u32 idx_zero_compare = (0 << 0) | (1 << 8) | (2 << 16) | (3 << 24);
+ u32 idx_one_compare = (4 << 0) | (5 << 8) | (6 << 16) | (7 << 24);
+ u32 idx_last_compare =
+ (((LRNG_DATA_NUM_VALUES - 4) & LRNG_DATA_SLOTSIZE_MASK) << 0) |
+ (((LRNG_DATA_NUM_VALUES - 3) & LRNG_DATA_SLOTSIZE_MASK) << 8) |
+ (((LRNG_DATA_NUM_VALUES - 2) & LRNG_DATA_SLOTSIZE_MASK) << 16) |
+ (((LRNG_DATA_NUM_VALUES - 1) & LRNG_DATA_SLOTSIZE_MASK) << 24);
+
+ (void)idx_one_compare;
+
+ /* "poison" the array to verify the operation of the zeroization */
+ lrng_data_selftest[0] = 0xffffffff;
+ lrng_data_selftest[1] = 0xffffffff;
+
+ lrng_data_process_selftest_insert(0);
+ /*
+ * Note, when using lrng_data_process_u32() on unaligned ptr,
+ * the first slots will go into next word, and the last slots go
+ * into the previous word.
+ */
+ lrng_data_process_selftest_u32((4 << 0) | (1 << 8) | (2 << 16) |
+ (3 << 24));
+ lrng_data_process_selftest_insert(5);
+ lrng_data_process_selftest_insert(6);
+ lrng_data_process_selftest_insert(7);
+
+ if ((lrng_data_selftest[0] != idx_zero_compare) ||
+ (lrng_data_selftest[1] != idx_one_compare))
+ goto err;
+
+ /* Reset for next test */
+ lrng_data_selftest[0] = 0;
+ lrng_data_selftest[1] = 0;
+ lrng_data_selftest_ptr = 0;
+
+ for (time = 0; time < LRNG_DATA_NUM_VALUES; time++)
+ lrng_data_process_selftest_insert(time);
+
+ if ((lrng_data_selftest[0] != idx_zero_compare) ||
+ (lrng_data_selftest[1] != idx_one_compare) ||
+ (lrng_data_selftest[LRNG_DATA_ARRAY_SIZE - 1] != idx_last_compare))
+ goto err;
+
+ return LRNG_SELFTEST_PASSED;
+
+err:
+ pr_err("LRNG data array self-test FAILED\n");
+ return LRNG_SEFLTEST_ERROR_TIME;
+}
+
+/* The test vectors are taken from crypto/testmgr.h */
+static unsigned int lrng_hash_selftest(void)
+{
+ SHASH_DESC_ON_STACK(shash, NULL);
+ const struct lrng_crypto_cb *crypto_cb = &lrng_cc20_crypto_cb;
+ static const u8 lrng_hash_selftest_result[] =
+#ifdef CONFIG_CRYPTO_LIB_SHA256
+ { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
+ 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
+ 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+ 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad };
+#else /* CONFIG_CRYPTO_LIB_SHA256 */
+ { 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e,
+ 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d };
+#endif /* CONFIG_CRYPTO_LIB_SHA256 */
+ static const u8 hash_input[] = { 0x61, 0x62, 0x63 }; /* "abc" */
+ u8 digest[sizeof(lrng_hash_selftest_result)] __aligned(sizeof(u32));
+
+ BUG_ON(sizeof(digest) != crypto_cb->lrng_hash_digestsize(NULL));
+
+ if (!crypto_cb->lrng_hash_init(shash, NULL) &&
+ !crypto_cb->lrng_hash_update(shash, hash_input,
+ sizeof(hash_input)) &&
+ !crypto_cb->lrng_hash_final(shash, digest) &&
+ !memcmp(digest, lrng_hash_selftest_result, sizeof(digest)))
+ return 0;
+
+ pr_err("LRNG %s Hash self-test FAILED\n", crypto_cb->lrng_hash_name());
+ return LRNG_SEFLTEST_ERROR_HASH;
+}
+
+/*
+ * The test vectors were generated using the ChaCha20 DRNG from
+ * https://www.chronox.de/chacha20.html
+ */
+static unsigned int lrng_chacha20_drng_selftest(void)
+{
+ const struct lrng_crypto_cb *crypto_cb = &lrng_cc20_crypto_cb;
+ u8 seed[CHACHA_KEY_SIZE * 2] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ };
+ struct chacha20_block chacha20;
+ int ret;
+ u8 outbuf[CHACHA_KEY_SIZE * 2] __aligned(sizeof(u32));
+
+ /*
+ * Expected result when ChaCha20 DRNG state is zero:
+ * * constants are set to "expand 32-byte k"
+ * * remaining state is 0
+ * and pulling one half ChaCha20 DRNG block.
+ */
+ static const u8 expected_halfblock[CHACHA_KEY_SIZE] = {
+ 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90,
+ 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28,
+ 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a,
+ 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7 };
+
+ /*
+ * Expected result when ChaCha20 DRNG state is zero:
+ * * constants are set to "expand 32-byte k"
+ * * remaining state is 0
+ * followed by a reseed with two keyblocks
+ * 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ * 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ * 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ * 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ * 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ * 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ * 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ * 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f
+ * and pulling one ChaCha20 DRNG block.
+ */
+ static const u8 expected_oneblock[CHACHA_KEY_SIZE * 2] = {
+ 0xe3, 0xb0, 0x8a, 0xcc, 0x34, 0xc3, 0x17, 0x0e,
+ 0xc3, 0xd8, 0xc3, 0x40, 0xe7, 0x73, 0xe9, 0x0d,
+ 0xd1, 0x62, 0xa3, 0x5d, 0x7d, 0xf2, 0xf1, 0x4a,
+ 0x24, 0x42, 0xb7, 0x1e, 0xb0, 0x05, 0x17, 0x07,
+ 0xb9, 0x35, 0x10, 0x69, 0x8b, 0x46, 0xfb, 0x51,
+ 0xe9, 0x91, 0x3f, 0x46, 0xf2, 0x4d, 0xea, 0xd0,
+ 0x81, 0xc1, 0x1b, 0xa9, 0x5d, 0x52, 0x91, 0x5f,
+ 0xcd, 0xdc, 0xc6, 0xd6, 0xc3, 0x7c, 0x50, 0x23 };
+
+ /*
+ * Expected result when ChaCha20 DRNG state is zero:
+ * * constants are set to "expand 32-byte k"
+ * * remaining state is 0
+ * followed by a reseed with one key block plus one byte
+ * 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ * 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ * 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ * 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ * 0x20
+ * and pulling less than one ChaCha20 DRNG block.
+ */
+ static const u8 expected_block_nonalinged[CHACHA_KEY_SIZE + 4] = {
+ 0x9c, 0xfc, 0x5e, 0x31, 0x21, 0x62, 0x11, 0x85,
+ 0xd3, 0x77, 0xd3, 0x69, 0x0f, 0xa8, 0x16, 0x55,
+ 0xb4, 0x4c, 0xf6, 0x52, 0xf3, 0xa8, 0x37, 0x99,
+ 0x38, 0x76, 0xa0, 0x66, 0xec, 0xbb, 0xce, 0xa9,
+ 0x9c, 0x95, 0xa1, 0xfd };
+
+ BUILD_BUG_ON(sizeof(seed) % sizeof(u32));
+
+ memset(&chacha20, 0, sizeof(chacha20));
+ lrng_cc20_init_rfc7539(&chacha20);
+ lrng_selftest_bswap32((u32 *)seed, sizeof(seed) / sizeof(u32));
+
+ /* Generate with zero state */
+ ret = crypto_cb->lrng_drng_generate_helper(&chacha20, outbuf,
+ sizeof(expected_halfblock));
+ if (ret != sizeof(expected_halfblock))
+ goto err;
+ if (memcmp(outbuf, expected_halfblock, sizeof(expected_halfblock)))
+ goto err;
+
+ /* Clear state of DRNG */
+ memset(&chacha20.key.u[0], 0, 48);
+
+ /* Reseed with 2 key blocks */
+ ret = crypto_cb->lrng_drng_seed_helper(&chacha20, seed,
+ sizeof(expected_oneblock));
+ if (ret < 0)
+ goto err;
+ ret = crypto_cb->lrng_drng_generate_helper(&chacha20, outbuf,
+ sizeof(expected_oneblock));
+ if (ret != sizeof(expected_oneblock))
+ goto err;
+ if (memcmp(outbuf, expected_oneblock, sizeof(expected_oneblock)))
+ goto err;
+
+ /* Clear state of DRNG */
+ memset(&chacha20.key.u[0], 0, 48);
+
+ /* Reseed with 1 key block and one byte */
+ ret = crypto_cb->lrng_drng_seed_helper(&chacha20, seed,
+ sizeof(expected_block_nonalinged));
+ if (ret < 0)
+ goto err;
+ ret = crypto_cb->lrng_drng_generate_helper(&chacha20, outbuf,
+ sizeof(expected_block_nonalinged));
+ if (ret != sizeof(expected_block_nonalinged))
+ goto err;
+ if (memcmp(outbuf, expected_block_nonalinged,
+ sizeof(expected_block_nonalinged)))
+ goto err;
+
+ return LRNG_SELFTEST_PASSED;
+
+err:
+ pr_err("LRNG ChaCha20 DRNG self-test FAILED\n");
+ return LRNG_SEFLTEST_ERROR_CHACHA20;
+}
+
+static int lrng_selftest(void)
+{
+ unsigned int ret = lrng_data_process_selftest();
+
+ ret |= lrng_chacha20_drng_selftest();
+ ret |= lrng_hash_selftest();
+
+ if (ret) {
+ if (IS_ENABLED(CONFIG_LRNG_SELFTEST_PANIC))
+ panic("LRNG self-tests failed: %u\n", ret);
+ } else {
+ pr_info("LRNG self-tests passed\n");
+ }
+
+ lrng_selftest_status = ret;
+
+ if (lrng_selftest_status)
+ return -EFAULT;
+ return 0;
+}
+
+#ifdef CONFIG_SYSFS
+/* Re-perform self-test when any value is written to the sysfs file. */
+static int lrng_selftest_sysfs_set(const char *val,
+ const struct kernel_param *kp)
+{
+ return lrng_selftest();
+}
+
+static const struct kernel_param_ops lrng_selftest_sysfs = {
+ .set = lrng_selftest_sysfs_set,
+ .get = param_get_uint,
+};
+module_param_cb(selftest_status, &lrng_selftest_sysfs, &lrng_selftest_status,
+ 0644);
+#endif /* CONFIG_SYSFS */
+
+static int __init lrng_selftest_init(void)
+{
+ return lrng_selftest();
+}
+
+module_init(lrng_selftest_init);
--
2.31.1




2021-07-14 05:52:55

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v41 12/13] LRNG - add interface for gathering of raw entropy

The test interface allows a privileged process to capture the raw
unconditioned noise that is collected by the LRNG for statistical
analysis. Such testing allows the analysis how much entropy
the interrupt noise source provides on a given platform.
Extracted noise data is not used to seed the LRNG. This
is a test interface and not appropriate for production systems.
Yet, the interface is considered to be sufficiently secured for
production systems.

Access to the data is given through the lrng_raw debugfs file. The
data buffer should be multiples of sizeof(u32) to fill the entire
buffer. Using the option lrng_testing.boot_test=1 the raw noise of
the first 1000 entropy events since boot can be sampled.

This test interface allows generating the data required for
analysis whether the LRNG is in compliance with SP800-90B
sections 3.1.3 and 3.1.4.

In addition, the test interface allows gathering of the concatenated raw
entropy data to verify that the concatenation works appropriately.
This includes sampling of the following raw data:

* high-resolution time stamp

* Jiffies

* IRQ number

* IRQ flags

* return instruction pointer

* interrupt register state

* array logic batching the high-resolution time stamp

* enabling the runtime configuration of entropy source entropy rates

Also, a testing interface to support ACVT of the hash implementation
is provided. The reason why only hash testing is supported (as
opposed to also provide testing for the DRNG) is the fact that the
LRNG software hash implementation contains glue code that may
warrant testing in addition to the testing of the software ciphers
via the kernel crypto API. Also, for testing the CTR-DRBG, the
underlying AES implementation would need to be tested. However,
such AES test interface cannot be provided by the LRNG as it has no
means to access the AES operation.

Finally, the execution duration for processing a time stamp can be
obtained with the LRNG raw entropy interface.

If a test interface is not compiled, its code is a noop which has no
impact on the performance.

CC: Torsten Duwe <[email protected]>
CC: "Eric W. Biederman" <[email protected]>
CC: "Alexander E. Patrakov" <[email protected]>
CC: "Ahmed S. Darwish" <[email protected]>
CC: "Theodore Y. Ts'o" <[email protected]>
CC: Willy Tarreau <[email protected]>
CC: Matthew Garrett <[email protected]>
CC: Vito Caputo <[email protected]>
CC: Andreas Dilger <[email protected]>
CC: Jan Kara <[email protected]>
CC: Ray Strode <[email protected]>
CC: William Jon McCann <[email protected]>
CC: zhangjs <[email protected]>
CC: Andy Lutomirski <[email protected]>
CC: Florian Weimer <[email protected]>
CC: Lennart Poettering <[email protected]>
CC: Nicolai Stange <[email protected]>
CC: Alexander Lobakin <[email protected]>
Reviewed-by: Roman Drahtmueller <[email protected]>
Tested-by: Marcelo Henrique Cerri <[email protected]>
Tested-by: Neil Horman <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Kconfig | 162 ++++++++
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_testing.c | 689 +++++++++++++++++++++++++++++++
3 files changed, 852 insertions(+)
create mode 100644 drivers/char/lrng/lrng_testing.c

diff --git a/drivers/char/lrng/Kconfig b/drivers/char/lrng/Kconfig
index 931959f75a83..379e89e7fa53 100644
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -323,4 +323,166 @@ config LRNG_KCAPI
provided by the selected kernel crypto API RNG.
endif # LRNG_DRNG_SWITCH

+menuconfig LRNG_TESTING_MENU
+ bool "LRNG testing interfaces"
+ depends on DEBUG_FS
+ help
+ Enable one or more of the following test interfaces.
+
+ If unsure, say N.
+
+if LRNG_TESTING_MENU
+
+config LRNG_RAW_HIRES_ENTROPY
+ bool "Enable entropy test interface to hires timer noise source"
+ default y
+ help
+ The test interface allows a privileged process to capture
+ the raw unconditioned high resolution time stamp noise that
+ is collected by the LRNG for statistical analysis. Extracted
+ noise data is not used to seed the LRNG.
+
+ The raw noise data can be obtained using the lrng_raw_hires
+ debugfs file. Using the option lrng_testing.boot_raw_hires_test=1
+ the raw noise of the first 1000 entropy events since boot
+ can be sampled.
+
+config LRNG_RAW_JIFFIES_ENTROPY
+ bool "Enable entropy test interface to Jiffies noise source"
+ help
+ The test interface allows a privileged process to capture
+ the raw unconditioned Jiffies that is collected by
+ the LRNG for statistical analysis. This data is used for
+ seeding the LRNG if a high-resolution time stamp is not
+ available. If a high-resolution time stamp is detected,
+ the Jiffies value is not collected by the LRNG and no
+ data is provided via the test interface. Extracted noise
+ data is not used to seed the random number generator.
+
+ The raw noise data can be obtained using the lrng_raw_jiffies
+ debugfs file. Using the option lrng_testing.boot_raw_jiffies_test=1
+ the raw noise of the first 1000 entropy events since boot
+ can be sampled.
+
+config LRNG_RAW_IRQ_ENTROPY
+ bool "Enable entropy test interface to IRQ number noise source"
+ help
+ The test interface allows a privileged process to capture
+ the raw unconditioned interrupt number that is collected by
+ the LRNG for statistical analysis. This data is used for
+ seeding the random32 PRNG external to the LRNG if a
+ high-resolution time stamp is available or it will be used to
+ seed the LRNG otherwise. Extracted noise data is not used to
+ seed the random number generator.
+
+ The raw noise data can be obtained using the lrng_raw_irq
+ debugfs file. Using the option lrng_testing.boot_raw_irq_test=1
+ the raw noise of the first 1000 entropy events since boot
+ can be sampled.
+
+config LRNG_RAW_IRQFLAGS_ENTROPY
+ bool "Enable entropy test interface to IRQ flags noise source"
+ help
+ The test interface allows a privileged process to capture
+ the raw unconditioned interrupt flags that is collected by
+ the LRNG for statistical analysis. This data is used for
+ seeding the random32 PRNG external to the LRNG if a
+ high-resolution time stamp is available or it will be used to
+ seed the LRNG otherwise. Extracted noise data is not used to
+ seed the random number generator.
+
+ The raw noise data can be obtained using the lrng_raw_irqflags
+ debugfs file. Using the option lrng_testing.boot_raw_irqflags_test=1
+ the raw noise of the first 1000 entropy events since boot
+ can be sampled.
+
+config LRNG_RAW_RETIP_ENTROPY
+ bool "Enable entropy test interface to RETIP value noise source"
+ help
+ The test interface allows a privileged process to capture
+ the raw unconditioned return instruction pointer value
+ that is collected by the LRNG for statistical analysis.
+ This data is used for seeding the random32 PRNG external
+ to the LRNG if a high-resolution time stamp is available or
+ it will be used to seed the LRNG otherwise. Extracted noise
+ data is not used to seed the random number generator.
+
+ The raw noise data can be obtained using the lrng_raw_retip
+ debugfs file. Using the option lrng_testing.boot_raw_retip_test=1
+ the raw noise of the first 1000 entropy events since boot
+ can be sampled.
+
+config LRNG_RAW_REGS_ENTROPY
+ bool "Enable entropy test interface to IRQ register value noise source"
+ help
+ The test interface allows a privileged process to capture
+ the raw unconditioned interrupt register value that is
+ collected by the LRNG for statistical analysis. Extracted noise
+ data is not used to seed the random number generator.
+
+ The raw noise data can be obtained using the lrng_raw_regs
+ debugfs file. Using the option lrng_testing.boot_raw_regs_test=1
+ the raw noise of the first 1000 entropy events since boot
+ can be sampled.
+
+config LRNG_RAW_ARRAY
+ bool "Enable test interface to LRNG raw entropy storage array"
+ help
+ The test interface allows a privileged process to capture
+ the raw noise data that is collected by the LRNG
+ in the per-CPU array for statistical analysis. The purpose
+ of this interface is to verify that the array handling code
+ truly only concatenates data and provides the same entropy
+ rate as the raw unconditioned noise source when assessing
+ the collected data byte-wise.
+
+ The data can be obtained using the lrng_raw_array debugfs
+ file. Using the option lrng_testing.boot_raw_array=1
+ the raw noise of the first 1000 entropy events since boot
+ can be sampled.
+
+config LRNG_IRQ_PERF
+ bool "Enable LRNG interrupt performance monitor"
+ help
+ With this option, the performance monitor of the LRNG
+ interrupt handling code is enabled. The file provides
+ the execution time of the interrupt handler in
+ cycles.
+
+ The interrupt performance data can be obtained using
+ the lrng_irq_perf debugfs file. Using the option
+ lrng_testing.boot_irq_perf=1 the performance data of
+ the first 1000 entropy events since boot can be sampled.
+
+config LRNG_ACVT_HASH
+ bool "Enable LRNG ACVT Hash interface"
+ help
+ With this option, the LRNG built-in hash function used for
+ auxiliary pool management and prior to switching the
+ cryptographic backends is made available for ACVT. The
+ interface allows writing of the data to be hashed
+ into the interface. The read operation triggers the hash
+ operation to generate message digest.
+
+ The ACVT interface is available with the lrng_acvt_hash
+ debugfs file.
+
+config LRNG_RUNTIME_ES_CONFIG
+ bool "Enable runtime configuration of entropy sources"
+ help
+ When enabling this option, the LRNG provides the mechanism
+ allowing to alter the entropy rate of each entropy source
+ during boot time and runtime.
+
+ The following interfaces are available:
+ lrng_archrandom.archrandom for the CPU entropy source,
+ lrng_jent.jitterrng for the Jitter RNG entropy source, and
+ lrng_sw_noise.irq_entropy for the interrupt entropy source.
+
+config LRNG_TESTING
+ bool
+ default y if (LRNG_RAW_HIRES_ENTROPY || LRNG_RAW_JIFFIES_ENTROPY ||LRNG_RAW_IRQ_ENTROPY || LRNG_RAW_IRQFLAGS_ENTROPY || LRNG_RAW_RETIP_ENTROPY || LRNG_RAW_REGS_ENTROPY || LRNG_RAW_ARRAY || LRNG_IRQ_PERF || LRNG_ACVT_HASH)
+
+endif #LRNG_TESTING_MENU
+
endif # LRNG
diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile
index 3c8637befb42..532501b38a00 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_LRNG_DRBG) += lrng_drbg.o
obj-$(CONFIG_LRNG_KCAPI) += lrng_kcapi.o
obj-$(CONFIG_LRNG_JENT) += lrng_jent.o
obj-$(CONFIG_LRNG_HEALTH_TESTS) += lrng_health.o
+obj-$(CONFIG_LRNG_TESTING) += lrng_testing.o
diff --git a/drivers/char/lrng/lrng_testing.c b/drivers/char/lrng/lrng_testing.c
new file mode 100644
index 000000000000..99cc35abf45c
--- /dev/null
+++ b/drivers/char/lrng/lrng_testing.c
@@ -0,0 +1,689 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Linux Random Number Generator (LRNG) testing interfaces
+ *
+ * Copyright (C) 2019 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/atomic.h>
+#include <linux/bug.h>
+#include <linux/debugfs.h>
+#include <linux/lrng.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <asm/errno.h>
+
+#include "lrng_internal.h"
+
+#define LRNG_TESTING_RINGBUFFER_SIZE 1024
+#define LRNG_TESTING_RINGBUFFER_MASK (LRNG_TESTING_RINGBUFFER_SIZE - 1)
+
+struct lrng_testing {
+ u32 lrng_testing_rb[LRNG_TESTING_RINGBUFFER_SIZE];
+ u32 rb_reader;
+ u32 rb_writer;
+ atomic_t lrng_testing_enabled;
+ spinlock_t lock;
+ wait_queue_head_t read_wait;
+};
+
+/*************************** Generic Data Handling ****************************/
+
+/*
+ * boot variable:
+ * 0 ==> No boot test, gathering of runtime data allowed
+ * 1 ==> Boot test enabled and ready for collecting data, gathering runtime
+ * data is disabled
+ * 2 ==> Boot test completed and disabled, gathering of runtime data is
+ * disabled
+ */
+
+static inline void lrng_testing_reset(struct lrng_testing *data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->lock, flags);
+ data->rb_reader = 0;
+ data->rb_writer = 0;
+ spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static inline void lrng_testing_init(struct lrng_testing *data, u32 boot)
+{
+ /*
+ * The boot time testing implies we have a running test. If the
+ * caller wants to clear it, he has to unset the boot_test flag
+ * at runtime via sysfs to enable regular runtime testing
+ */
+ if (boot)
+ return;
+
+ lrng_testing_reset(data);
+ atomic_set(&data->lrng_testing_enabled, 1);
+ pr_warn("Enabling data collection\n");
+}
+
+static inline void lrng_testing_fini(struct lrng_testing *data, u32 boot)
+{
+ /* If we have boot data, we do not reset yet to allow data to be read */
+ if (boot)
+ return;
+
+ atomic_set(&data->lrng_testing_enabled, 0);
+ lrng_testing_reset(data);
+ pr_warn("Disabling data collection\n");
+}
+
+static inline bool lrng_testing_store(struct lrng_testing *data, u32 value,
+ u32 *boot)
+{
+ unsigned long flags;
+
+ if (!atomic_read(&data->lrng_testing_enabled) && (*boot != 1))
+ return false;
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ /*
+ * Disable entropy testing for boot time testing after ring buffer
+ * is filled.
+ */
+ if (*boot) {
+ if (data->rb_writer > LRNG_TESTING_RINGBUFFER_SIZE) {
+ *boot = 2;
+ pr_warn_once("One time data collection test disabled\n");
+ spin_unlock_irqrestore(&data->lock, flags);
+ return false;
+ }
+
+ if (data->rb_writer == 1)
+ pr_warn("One time data collection test enabled\n");
+ }
+
+ data->lrng_testing_rb[data->rb_writer & LRNG_TESTING_RINGBUFFER_MASK] =
+ value;
+ data->rb_writer++;
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ if (wq_has_sleeper(&data->read_wait))
+ wake_up_interruptible(&data->read_wait);
+
+ return true;
+}
+
+static inline bool lrng_testing_have_data(struct lrng_testing *data)
+{
+ return ((data->rb_writer & LRNG_TESTING_RINGBUFFER_MASK) !=
+ (data->rb_reader & LRNG_TESTING_RINGBUFFER_MASK));
+}
+
+static inline int lrng_testing_reader(struct lrng_testing *data, u32 *boot,
+ u8 *outbuf, u32 outbuflen)
+{
+ unsigned long flags;
+ int collected_data = 0;
+
+ lrng_testing_init(data, *boot);
+
+ while (outbuflen) {
+ spin_lock_irqsave(&data->lock, flags);
+
+ /* We have no data or reached the writer. */
+ if (!data->rb_writer ||
+ (data->rb_writer == data->rb_reader)) {
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ /*
+ * Now we gathered all boot data, enable regular data
+ * collection.
+ */
+ if (*boot) {
+ *boot = 0;
+ goto out;
+ }
+
+ wait_event_interruptible(data->read_wait,
+ lrng_testing_have_data(data));
+ if (signal_pending(current)) {
+ collected_data = -ERESTARTSYS;
+ goto out;
+ }
+
+ continue;
+ }
+
+ /* We copy out word-wise */
+ if (outbuflen < sizeof(u32)) {
+ spin_unlock_irqrestore(&data->lock, flags);
+ goto out;
+ }
+
+ memcpy(outbuf, &data->lrng_testing_rb[data->rb_reader],
+ sizeof(u32));
+ data->rb_reader++;
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ outbuf += sizeof(u32);
+ outbuflen -= sizeof(u32);
+ collected_data += sizeof(u32);
+ }
+
+out:
+ lrng_testing_fini(data, *boot);
+ return collected_data;
+}
+
+static int lrng_testing_extract_user(struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos,
+ int (*reader)(u8 *outbuf, u32 outbuflen))
+{
+ u8 *tmp, *tmp_aligned;
+ int ret = 0, large_request = (nbytes > 256);
+
+ if (!nbytes)
+ return 0;
+
+ /*
+ * The intention of this interface is for collecting at least
+ * 1000 samples due to the SP800-90B requirements. So, we make no
+ * effort in avoiding allocating more memory that actually needed
+ * by the user. Hence, we allocate sufficient memory to always hold
+ * that amount of data.
+ */
+ tmp = kmalloc(LRNG_TESTING_RINGBUFFER_SIZE + sizeof(u32), GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ tmp_aligned = PTR_ALIGN(tmp, sizeof(u32));
+
+ while (nbytes) {
+ int i;
+
+ if (large_request && need_resched()) {
+ if (signal_pending(current)) {
+ if (ret == 0)
+ ret = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+
+ i = min_t(int, nbytes, LRNG_TESTING_RINGBUFFER_SIZE);
+ i = reader(tmp_aligned, i);
+ if (i <= 0) {
+ if (i < 0)
+ ret = i;
+ break;
+ }
+ if (copy_to_user(buf, tmp_aligned, i)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ nbytes -= i;
+ buf += i;
+ ret += i;
+ }
+
+ kfree_sensitive(tmp);
+
+ if (ret > 0)
+ *ppos += ret;
+
+ return ret;
+}
+
+/************** Raw High-Resolution Timer Entropy Data Handling ***************/
+
+#ifdef CONFIG_LRNG_RAW_HIRES_ENTROPY
+
+static u32 boot_raw_hires_test = 0;
+module_param(boot_raw_hires_test, uint, 0644);
+MODULE_PARM_DESC(boot_raw_hires_test, "Enable gathering boot time high resolution timer entropy of the first entropy events");
+
+static struct lrng_testing lrng_raw_hires = {
+ .rb_reader = 0,
+ .rb_writer = 0,
+ .lock = __SPIN_LOCK_UNLOCKED(lrng_raw_hires.lock),
+ .read_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lrng_raw_hires.read_wait)
+};
+
+bool lrng_raw_hires_entropy_store(u32 value)
+{
+ return lrng_testing_store(&lrng_raw_hires, value, &boot_raw_hires_test);
+}
+
+static int lrng_raw_hires_entropy_reader(u8 *outbuf, u32 outbuflen)
+{
+ return lrng_testing_reader(&lrng_raw_hires, &boot_raw_hires_test,
+ outbuf, outbuflen);
+}
+
+static ssize_t lrng_raw_hires_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ return lrng_testing_extract_user(file, to, count, ppos,
+ lrng_raw_hires_entropy_reader);
+}
+
+static const struct file_operations lrng_raw_hires_fops = {
+ .owner = THIS_MODULE,
+ .read = lrng_raw_hires_read,
+};
+
+#endif /* CONFIG_LRNG_RAW_HIRES_ENTROPY */
+
+/********************* Raw Jiffies Entropy Data Handling **********************/
+
+#ifdef CONFIG_LRNG_RAW_JIFFIES_ENTROPY
+
+static u32 boot_raw_jiffies_test = 0;
+module_param(boot_raw_jiffies_test, uint, 0644);
+MODULE_PARM_DESC(boot_raw_jiffies_test, "Enable gathering boot time high resolution timer entropy of the first entropy events");
+
+static struct lrng_testing lrng_raw_jiffies = {
+ .rb_reader = 0,
+ .rb_writer = 0,
+ .lock = __SPIN_LOCK_UNLOCKED(lrng_raw_jiffies.lock),
+ .read_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lrng_raw_jiffies.read_wait)
+};
+
+bool lrng_raw_jiffies_entropy_store(u32 value)
+{
+ return lrng_testing_store(&lrng_raw_jiffies, value,
+ &boot_raw_jiffies_test);
+}
+
+static int lrng_raw_jiffies_entropy_reader(u8 *outbuf, u32 outbuflen)
+{
+ return lrng_testing_reader(&lrng_raw_jiffies, &boot_raw_jiffies_test,
+ outbuf, outbuflen);
+}
+
+static ssize_t lrng_raw_jiffies_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ return lrng_testing_extract_user(file, to, count, ppos,
+ lrng_raw_jiffies_entropy_reader);
+}
+
+static const struct file_operations lrng_raw_jiffies_fops = {
+ .owner = THIS_MODULE,
+ .read = lrng_raw_jiffies_read,
+};
+
+#endif /* CONFIG_LRNG_RAW_JIFFIES_ENTROPY */
+
+/************************** Raw IRQ Data Handling ****************************/
+
+#ifdef CONFIG_LRNG_RAW_IRQ_ENTROPY
+
+static u32 boot_raw_irq_test = 0;
+module_param(boot_raw_irq_test, uint, 0644);
+MODULE_PARM_DESC(boot_raw_irq_test, "Enable gathering boot time entropy of the first IRQ entropy events");
+
+static struct lrng_testing lrng_raw_irq = {
+ .rb_reader = 0,
+ .rb_writer = 0,
+ .lock = __SPIN_LOCK_UNLOCKED(lrng_raw_irq.lock),
+ .read_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lrng_raw_irq.read_wait)
+};
+
+bool lrng_raw_irq_entropy_store(u32 value)
+{
+ return lrng_testing_store(&lrng_raw_irq, value, &boot_raw_irq_test);
+}
+
+static int lrng_raw_irq_entropy_reader(u8 *outbuf, u32 outbuflen)
+{
+ return lrng_testing_reader(&lrng_raw_irq, &boot_raw_irq_test, outbuf,
+ outbuflen);
+}
+
+static ssize_t lrng_raw_irq_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ return lrng_testing_extract_user(file, to, count, ppos,
+ lrng_raw_irq_entropy_reader);
+}
+
+static const struct file_operations lrng_raw_irq_fops = {
+ .owner = THIS_MODULE,
+ .read = lrng_raw_irq_read,
+};
+
+#endif /* CONFIG_LRNG_RAW_IRQ_ENTROPY */
+
+/************************ Raw IRQFLAGS Data Handling **************************/
+
+#ifdef CONFIG_LRNG_RAW_IRQFLAGS_ENTROPY
+
+static u32 boot_raw_irqflags_test = 0;
+module_param(boot_raw_irqflags_test, uint, 0644);
+MODULE_PARM_DESC(boot_raw_irqflags_test, "Enable gathering boot time entropy of the first IRQ flags entropy events");
+
+static struct lrng_testing lrng_raw_irqflags = {
+ .rb_reader = 0,
+ .rb_writer = 0,
+ .lock = __SPIN_LOCK_UNLOCKED(lrng_raw_irqflags.lock),
+ .read_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lrng_raw_irqflags.read_wait)
+};
+
+bool lrng_raw_irqflags_entropy_store(u32 value)
+{
+ return lrng_testing_store(&lrng_raw_irqflags, value,
+ &boot_raw_irqflags_test);
+}
+
+static int lrng_raw_irqflags_entropy_reader(u8 *outbuf, u32 outbuflen)
+{
+ return lrng_testing_reader(&lrng_raw_irqflags, &boot_raw_irqflags_test,
+ outbuf, outbuflen);
+}
+
+static ssize_t lrng_raw_irqflags_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ return lrng_testing_extract_user(file, to, count, ppos,
+ lrng_raw_irqflags_entropy_reader);
+}
+
+static const struct file_operations lrng_raw_irqflags_fops = {
+ .owner = THIS_MODULE,
+ .read = lrng_raw_irqflags_read,
+};
+
+#endif /* CONFIG_LRNG_RAW_IRQFLAGS_ENTROPY */
+
+/************************ Raw _RET_IP_ Data Handling **************************/
+
+#ifdef CONFIG_LRNG_RAW_RETIP_ENTROPY
+
+static u32 boot_raw_retip_test = 0;
+module_param(boot_raw_retip_test, uint, 0644);
+MODULE_PARM_DESC(boot_raw_retip_test, "Enable gathering boot time entropy of the first return instruction pointer entropy events");
+
+static struct lrng_testing lrng_raw_retip = {
+ .rb_reader = 0,
+ .rb_writer = 0,
+ .lock = __SPIN_LOCK_UNLOCKED(lrng_raw_retip.lock),
+ .read_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lrng_raw_retip.read_wait)
+};
+
+bool lrng_raw_retip_entropy_store(u32 value)
+{
+ return lrng_testing_store(&lrng_raw_retip, value, &boot_raw_retip_test);
+}
+
+static int lrng_raw_retip_entropy_reader(u8 *outbuf, u32 outbuflen)
+{
+ return lrng_testing_reader(&lrng_raw_retip, &boot_raw_retip_test,
+ outbuf, outbuflen);
+}
+
+static ssize_t lrng_raw_retip_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ return lrng_testing_extract_user(file, to, count, ppos,
+ lrng_raw_retip_entropy_reader);
+}
+
+static const struct file_operations lrng_raw_retip_fops = {
+ .owner = THIS_MODULE,
+ .read = lrng_raw_retip_read,
+};
+
+#endif /* CONFIG_LRNG_RAW_RETIP_ENTROPY */
+
+/********************** Raw IRQ register Data Handling ************************/
+
+#ifdef CONFIG_LRNG_RAW_REGS_ENTROPY
+
+static u32 boot_raw_regs_test = 0;
+module_param(boot_raw_regs_test, uint, 0644);
+MODULE_PARM_DESC(boot_raw_regs_test, "Enable gathering boot time entropy of the first interrupt register entropy events");
+
+static struct lrng_testing lrng_raw_regs = {
+ .rb_reader = 0,
+ .rb_writer = 0,
+ .lock = __SPIN_LOCK_UNLOCKED(lrng_raw_regs.lock),
+ .read_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lrng_raw_regs.read_wait)
+};
+
+bool lrng_raw_regs_entropy_store(u32 value)
+{
+ return lrng_testing_store(&lrng_raw_regs, value, &boot_raw_regs_test);
+}
+
+static int lrng_raw_regs_entropy_reader(u8 *outbuf, u32 outbuflen)
+{
+ return lrng_testing_reader(&lrng_raw_regs, &boot_raw_regs_test,
+ outbuf, outbuflen);
+}
+
+static ssize_t lrng_raw_regs_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ return lrng_testing_extract_user(file, to, count, ppos,
+ lrng_raw_regs_entropy_reader);
+}
+
+static const struct file_operations lrng_raw_regs_fops = {
+ .owner = THIS_MODULE,
+ .read = lrng_raw_regs_read,
+};
+
+#endif /* CONFIG_LRNG_RAW_REGS_ENTROPY */
+
+/********************** Raw Entropy Array Data Handling ***********************/
+
+#ifdef CONFIG_LRNG_RAW_ARRAY
+
+static u32 boot_raw_array = 0;
+module_param(boot_raw_array, uint, 0644);
+MODULE_PARM_DESC(boot_raw_array, "Enable gathering boot time raw noise array data of the first entropy events");
+
+static struct lrng_testing lrng_raw_array = {
+ .rb_reader = 0,
+ .rb_writer = 0,
+ .lock = __SPIN_LOCK_UNLOCKED(lrng_raw_array.lock),
+ .read_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lrng_raw_array.read_wait)
+};
+
+bool lrng_raw_array_entropy_store(u32 value)
+{
+ return lrng_testing_store(&lrng_raw_array, value, &boot_raw_array);
+}
+
+static int lrng_raw_array_entropy_reader(u8 *outbuf, u32 outbuflen)
+{
+ return lrng_testing_reader(&lrng_raw_array, &boot_raw_array, outbuf,
+ outbuflen);
+}
+
+static ssize_t lrng_raw_array_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ return lrng_testing_extract_user(file, to, count, ppos,
+ lrng_raw_array_entropy_reader);
+}
+
+static const struct file_operations lrng_raw_array_fops = {
+ .owner = THIS_MODULE,
+ .read = lrng_raw_array_read,
+};
+
+#endif /* CONFIG_LRNG_RAW_ARRAY */
+
+/******************** Interrupt Performance Data Handling *********************/
+
+#ifdef CONFIG_LRNG_IRQ_PERF
+
+static u32 boot_irq_perf = 0;
+module_param(boot_irq_perf, uint, 0644);
+MODULE_PARM_DESC(boot_irq_perf, "Enable gathering boot time interrupt performance data of the first entropy events");
+
+static struct lrng_testing lrng_irq_perf = {
+ .rb_reader = 0,
+ .rb_writer = 0,
+ .lock = __SPIN_LOCK_UNLOCKED(lrng_irq_perf.lock),
+ .read_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lrng_irq_perf.read_wait)
+};
+
+bool lrng_perf_time(u32 start)
+{
+ return lrng_testing_store(&lrng_irq_perf, random_get_entropy() - start,
+ &boot_irq_perf);
+}
+
+static int lrng_irq_perf_reader(u8 *outbuf, u32 outbuflen)
+{
+ return lrng_testing_reader(&lrng_irq_perf, &boot_irq_perf, outbuf,
+ outbuflen);
+}
+
+static ssize_t lrng_irq_perf_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ return lrng_testing_extract_user(file, to, count, ppos,
+ lrng_irq_perf_reader);
+}
+
+static const struct file_operations lrng_irq_perf_fops = {
+ .owner = THIS_MODULE,
+ .read = lrng_irq_perf_read,
+};
+
+#endif /* CONFIG_LRNG_IRQ_PERF */
+
+/*********************************** ACVT ************************************/
+
+#ifdef CONFIG_LRNG_ACVT_HASH
+
+/* maximum amount of data to be hashed as defined by ACVP */
+#define LRNG_ACVT_MAX_SHA_MSG (65536 >> 3)
+
+/*
+ * As we use static variables to store the data, it is clear that the
+ * test interface is only able to handle single threaded testing. This is
+ * considered to be sufficient for testing. If multi-threaded use of the
+ * ACVT test interface would be performed, the caller would get garbage
+ * but the kernel operation is unaffected by this.
+ */
+static u8 lrng_acvt_hash_data[LRNG_ACVT_MAX_SHA_MSG]
+ __aligned(LRNG_KCAPI_ALIGN);
+static atomic_t lrng_acvt_hash_data_size = ATOMIC_INIT(0);
+static u8 lrng_acvt_hash_digest[LRNG_ATOMIC_DIGEST_SIZE];
+
+static ssize_t lrng_acvt_hash_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ if (nbytes > LRNG_ACVT_MAX_SHA_MSG)
+ return -EINVAL;
+
+ atomic_set(&lrng_acvt_hash_data_size, (int)nbytes);
+
+ return simple_write_to_buffer(lrng_acvt_hash_data,
+ LRNG_ACVT_MAX_SHA_MSG, ppos, buf, nbytes);
+}
+
+static ssize_t lrng_acvt_hash_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ SHASH_DESC_ON_STACK(shash, NULL);
+ const struct lrng_crypto_cb *crypto_cb = &lrng_cc20_crypto_cb;
+ ssize_t ret;
+
+ if (count > LRNG_ATOMIC_DIGEST_SIZE)
+ return -EINVAL;
+
+ ret = crypto_cb->lrng_hash_init(shash, NULL) ?:
+ crypto_cb->lrng_hash_update(shash, lrng_acvt_hash_data,
+ atomic_read_u32(&lrng_acvt_hash_data_size)) ?:
+ crypto_cb->lrng_hash_final(shash, lrng_acvt_hash_digest);
+ if (ret)
+ return ret;
+
+ return simple_read_from_buffer(to, count, ppos, lrng_acvt_hash_digest,
+ sizeof(lrng_acvt_hash_digest));
+}
+
+static const struct file_operations lrng_acvt_hash_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .llseek = default_llseek,
+ .read = lrng_acvt_hash_read,
+ .write = lrng_acvt_hash_write,
+};
+
+#endif /* CONFIG_LRNG_ACVT_DRNG */
+
+/**************************************************************************
+ * Debugfs interface
+ **************************************************************************/
+
+static int __init lrng_raw_init(void)
+{
+ struct dentry *lrng_raw_debugfs_root;
+
+ lrng_raw_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
+
+#ifdef CONFIG_LRNG_RAW_HIRES_ENTROPY
+ debugfs_create_file_unsafe("lrng_raw_hires", 0400,
+ lrng_raw_debugfs_root, NULL,
+ &lrng_raw_hires_fops);
+#endif
+#ifdef CONFIG_LRNG_RAW_JIFFIES_ENTROPY
+ debugfs_create_file_unsafe("lrng_raw_jiffies", 0400,
+ lrng_raw_debugfs_root, NULL,
+ &lrng_raw_jiffies_fops);
+#endif
+#ifdef CONFIG_LRNG_RAW_IRQ_ENTROPY
+ debugfs_create_file_unsafe("lrng_raw_irq", 0400, lrng_raw_debugfs_root,
+ NULL, &lrng_raw_irq_fops);
+#endif
+#ifdef CONFIG_LRNG_RAW_IRQFLAGS_ENTROPY
+ debugfs_create_file_unsafe("lrng_raw_irqflags", 0400,
+ lrng_raw_debugfs_root, NULL,
+ &lrng_raw_irqflags_fops);
+#endif
+#ifdef CONFIG_LRNG_RAW_RETIP_ENTROPY
+ debugfs_create_file_unsafe("lrng_raw_retip", 0400,
+ lrng_raw_debugfs_root, NULL,
+ &lrng_raw_retip_fops);
+#endif
+#ifdef CONFIG_LRNG_RAW_REGS_ENTROPY
+ debugfs_create_file_unsafe("lrng_raw_regs", 0400,
+ lrng_raw_debugfs_root, NULL,
+ &lrng_raw_regs_fops);
+#endif
+#ifdef CONFIG_LRNG_RAW_ARRAY
+ debugfs_create_file_unsafe("lrng_raw_array", 0400,
+ lrng_raw_debugfs_root, NULL,
+ &lrng_raw_array_fops);
+#endif
+#ifdef CONFIG_LRNG_IRQ_PERF
+ debugfs_create_file_unsafe("lrng_irq_perf", 0400, lrng_raw_debugfs_root,
+ NULL, &lrng_irq_perf_fops);
+#endif
+#ifdef CONFIG_LRNG_ACVT_HASH
+ debugfs_create_file_unsafe("lrng_acvt_hash", 0600,
+ lrng_raw_debugfs_root, NULL,
+ &lrng_acvt_hash_fops);
+#endif
+
+ return 0;
+}
+
+module_init(lrng_raw_init);
--
2.31.1




2021-07-14 05:52:58

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v41 02/13] LRNG - allocate one DRNG instance per NUMA node

In order to improve NUMA-locality when serving getrandom(2) requests,
allocate one DRNG instance per node.

The DRNG instance that is present right from the start of the kernel is
reused as the first per-NUMA-node DRNG. For all remaining online NUMA
nodes a new DRNG instance is allocated.

During boot time, the multiple DRNG instances are seeded sequentially.
With this, the first DRNG instance (referenced as the initial DRNG
in the code) is completely seeded with 256 bits of entropy before the
next DRNG instance is completely seeded.

When random numbers are requested, the NUMA-node-local DRNG is checked
whether it has been already fully seeded. If this is not the case, the
initial DRNG is used to serve the request.

CC: Torsten Duwe <[email protected]>
CC: "Eric W. Biederman" <[email protected]>
CC: "Alexander E. Patrakov" <[email protected]>
CC: "Ahmed S. Darwish" <[email protected]>
CC: "Theodore Y. Ts'o" <[email protected]>
CC: Willy Tarreau <[email protected]>
CC: Matthew Garrett <[email protected]>
CC: Vito Caputo <[email protected]>
CC: Andreas Dilger <[email protected]>
CC: Jan Kara <[email protected]>
CC: Ray Strode <[email protected]>
CC: William Jon McCann <[email protected]>
CC: zhangjs <[email protected]>
CC: Andy Lutomirski <[email protected]>
CC: Florian Weimer <[email protected]>
CC: Lennart Poettering <[email protected]>
CC: Nicolai Stange <[email protected]>
CC: Eric Biggers <[email protected]>
CC: Alexander Lobakin <[email protected]>
Reviewed-by: Marcelo Henrique Cerri <[email protected]>
Reviewed-by: Roman Drahtmueller <[email protected]>
Tested-by: Marcelo Henrique Cerri <[email protected]>
Tested-by: Neil Horman <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Makefile | 2 +
drivers/char/lrng/lrng_internal.h | 5 ++
drivers/char/lrng/lrng_numa.c | 122 ++++++++++++++++++++++++++++++
3 files changed, 129 insertions(+)
create mode 100644 drivers/char/lrng/lrng_numa.c

diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile
index e72e01c15bb9..29724c65287d 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -7,3 +7,5 @@ obj-y += lrng_pool.o lrng_aux.o \
lrng_sw_noise.o lrng_archrandom.o \
lrng_drng.o lrng_chacha20.o \
lrng_interfaces.o
+
+obj-$(CONFIG_NUMA) += lrng_numa.o
diff --git a/drivers/char/lrng/lrng_internal.h b/drivers/char/lrng/lrng_internal.h
index 5f0d3602412e..e1d83c888894 100644
--- a/drivers/char/lrng/lrng_internal.h
+++ b/drivers/char/lrng/lrng_internal.h
@@ -254,8 +254,13 @@ int lrng_drng_get_sleep(u8 *outbuf, u32 outbuflen);
void lrng_drng_force_reseed(void);
void lrng_drng_seed_work(struct work_struct *dummy);

+#ifdef CONFIG_NUMA
+struct lrng_drng **lrng_drng_instances(void);
+void lrng_drngs_numa_alloc(void);
+#else /* CONFIG_NUMA */
static inline struct lrng_drng **lrng_drng_instances(void) { return NULL; }
static inline void lrng_drngs_numa_alloc(void) { return; }
+#endif /* CONFIG_NUMA */

/************************* Entropy sources management *************************/

diff --git a/drivers/char/lrng/lrng_numa.c b/drivers/char/lrng/lrng_numa.c
new file mode 100644
index 000000000000..fbfb40a5fb8d
--- /dev/null
+++ b/drivers/char/lrng/lrng_numa.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG NUMA support
+ *
+ * Copyright (C) 2016 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/lrng.h>
+#include <linux/slab.h>
+
+#include "lrng_internal.h"
+
+static struct lrng_drng **lrng_drng __read_mostly = NULL;
+
+struct lrng_drng **lrng_drng_instances(void)
+{
+ return smp_load_acquire(&lrng_drng);
+}
+
+/* Allocate the data structures for the per-NUMA node DRNGs */
+static void _lrng_drngs_numa_alloc(struct work_struct *work)
+{
+ struct lrng_drng **drngs;
+ struct lrng_drng *lrng_drng_init = lrng_drng_init_instance();
+ u32 node;
+ bool init_drng_used = false;
+
+ mutex_lock(&lrng_crypto_cb_update);
+
+ /* per-NUMA-node DRNGs are already present */
+ if (lrng_drng)
+ goto unlock;
+
+ drngs = kcalloc(nr_node_ids, sizeof(void *), GFP_KERNEL|__GFP_NOFAIL);
+ for_each_online_node(node) {
+ struct lrng_drng *drng;
+
+ if (!init_drng_used) {
+ drngs[node] = lrng_drng_init;
+ init_drng_used = true;
+ continue;
+ }
+
+ drng = kmalloc_node(sizeof(struct lrng_drng),
+ GFP_KERNEL|__GFP_NOFAIL, node);
+ memset(drng, 0, sizeof(lrng_drng));
+
+ drng->crypto_cb = lrng_drng_init->crypto_cb;
+ drng->drng = drng->crypto_cb->lrng_drng_alloc(
+ LRNG_DRNG_SECURITY_STRENGTH_BYTES);
+ if (IS_ERR(drng->drng)) {
+ kfree(drng);
+ goto err;
+ }
+
+ drng->hash = drng->crypto_cb->lrng_hash_alloc();
+ if (IS_ERR(drng->hash)) {
+ drng->crypto_cb->lrng_drng_dealloc(drng->drng);
+ kfree(drng);
+ goto err;
+ }
+
+ mutex_init(&drng->lock);
+ spin_lock_init(&drng->spin_lock);
+ rwlock_init(&drng->hash_lock);
+
+ /*
+ * Switch the hash used by the per-CPU pool.
+ * We do not need to lock the new hash as it is not usable yet
+ * due to **drngs not yet being initialized.
+ */
+ if (lrng_pcpu_switch_hash(node, drng->crypto_cb, drng->hash,
+ &lrng_cc20_crypto_cb))
+ goto err;
+
+ /*
+ * No reseeding of NUMA DRNGs from previous DRNGs as this
+ * would complicate the code. Let it simply reseed.
+ */
+ lrng_drng_reset(drng);
+ drngs[node] = drng;
+
+ lrng_pool_inc_numa_node();
+ pr_info("DRNG and entropy pool read hash for NUMA node %d allocated\n",
+ node);
+ }
+
+ /* counterpart to smp_load_acquire in lrng_drng_instances */
+ if (!cmpxchg_release(&lrng_drng, NULL, drngs)) {
+ lrng_pool_all_numa_nodes_seeded(false);
+ goto unlock;
+ }
+
+err:
+ for_each_online_node(node) {
+ struct lrng_drng *drng = drngs[node];
+
+ if (drng == lrng_drng_init)
+ continue;
+
+ if (drng) {
+ lrng_pcpu_switch_hash(node, &lrng_cc20_crypto_cb, NULL,
+ drng->crypto_cb);
+ drng->crypto_cb->lrng_hash_dealloc(drng->hash);
+ drng->crypto_cb->lrng_drng_dealloc(drng->drng);
+ kfree(drng);
+ }
+ }
+ kfree(drngs);
+
+unlock:
+ mutex_unlock(&lrng_crypto_cb_update);
+}
+
+static DECLARE_WORK(lrng_drngs_numa_alloc_work, _lrng_drngs_numa_alloc);
+
+void lrng_drngs_numa_alloc(void)
+{
+ schedule_work(&lrng_drngs_numa_alloc_work);
+}
--
2.31.1




2021-07-14 05:53:00

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v41 10/13] LRNG - add Jitter RNG fast noise source

The Jitter RNG fast noise source implemented as part of the kernel
crypto API is queried for 256 bits of entropy at the time the seed
buffer managed by the LRNG is about to be filled.

CC: Torsten Duwe <[email protected]>
CC: "Eric W. Biederman" <[email protected]>
CC: "Alexander E. Patrakov" <[email protected]>
CC: "Ahmed S. Darwish" <[email protected]>
CC: "Theodore Y. Ts'o" <[email protected]>
CC: Willy Tarreau <[email protected]>
CC: Matthew Garrett <[email protected]>
CC: Vito Caputo <[email protected]>
CC: Andreas Dilger <[email protected]>
CC: Jan Kara <[email protected]>
CC: Ray Strode <[email protected]>
CC: William Jon McCann <[email protected]>
CC: zhangjs <[email protected]>
CC: Andy Lutomirski <[email protected]>
CC: Florian Weimer <[email protected]>
CC: Lennart Poettering <[email protected]>
CC: Nicolai Stange <[email protected]>
CC: Alexander Lobakin <[email protected]>
Reviewed-by: Marcelo Henrique Cerri <[email protected]>
Tested-by: Marcelo Henrique Cerri <[email protected]>
Tested-by: Neil Horman <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Kconfig | 27 +++++++++++
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_jent.c | 90 +++++++++++++++++++++++++++++++++++
3 files changed, 118 insertions(+)
create mode 100644 drivers/char/lrng/lrng_jent.c

diff --git a/drivers/char/lrng/Kconfig b/drivers/char/lrng/Kconfig
index ffd2df43f2d4..e622b8532e2b 100644
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -182,6 +182,33 @@ config LRNG_IRQ_ENTROPY_RATE
interrupt entropy source will still deliver data but without
being credited with entropy.

+comment "Jitter RNG Entropy Source"
+
+config LRNG_JENT
+ bool "Enable Jitter RNG as LRNG Seed Source"
+ depends on CRYPTO
+ select CRYPTO_JITTERENTROPY
+ help
+ The Linux RNG may use the Jitter RNG as noise source. Enabling
+ this option enables the use of the Jitter RNG. Its default
+ entropy level is 16 bits of entropy per 256 data bits delivered
+ by the Jitter RNG. This entropy level can be changed at boot
+ time or at runtime with the lrng_base.jitterrng configuration
+ variable.
+
+config LRNG_JENT_ENTROPY_RATE
+ int "Jitter RNG Entropy Source Entropy Rate"
+ range 0 256
+ default 16
+ help
+ The option defines the amount of entropy the LRNG applies to 256
+ bits of data obtained from the Jitter RNG entropy source. The
+ LRNG enforces the limit that this value must be in the range
+ between 0 and 256.
+
+ In order to disable the Jitter RNG entropy source, the option
+ has to be set to 0.
+
comment "CPU Entropy Source"

config LRNG_CPU_ENTROPY_RATE
diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile
index 97d2b13d3227..6be88156010a 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_LRNG_DRNG_SWITCH) += lrng_switch.o
obj-$(CONFIG_LRNG_KCAPI_HASH) += lrng_kcapi_hash.o
obj-$(CONFIG_LRNG_DRBG) += lrng_drbg.o
obj-$(CONFIG_LRNG_KCAPI) += lrng_kcapi.o
+obj-$(CONFIG_LRNG_JENT) += lrng_jent.o
diff --git a/drivers/char/lrng/lrng_jent.c b/drivers/char/lrng/lrng_jent.c
new file mode 100644
index 000000000000..2599ab9352b6
--- /dev/null
+++ b/drivers/char/lrng/lrng_jent.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG Fast Entropy Source: Jitter RNG
+ *
+ * Copyright (C) 2016 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/types.h>
+#include <crypto/internal/jitterentropy.h>
+
+#include "lrng_internal.h"
+
+/*
+ * Estimated entropy of data is a 16th of LRNG_DRNG_SECURITY_STRENGTH_BITS.
+ * Albeit a full entropy assessment is provided for the noise source indicating
+ * that it provides high entropy rates and considering that it deactivates
+ * when it detects insufficient hardware, the chosen under estimation of
+ * entropy is considered to be acceptable to all reviewers.
+ */
+static u32 jitterrng = CONFIG_LRNG_JENT_ENTROPY_RATE;
+#ifdef CONFIG_LRNG_RUNTIME_ES_CONFIG
+module_param(jitterrng, uint, 0644);
+MODULE_PARM_DESC(jitterrng, "Entropy in bits of 256 data bits from Jitter RNG noise source");
+#endif
+
+static bool lrng_jent_initialized = false;
+static struct rand_data *lrng_jent_state;
+
+static int __init lrng_jent_initialize(void)
+{
+ /* Initialize the Jitter RNG after the clocksources are initialized. */
+ lrng_jent_state = jent_lrng_entropy_collector();
+ if (!lrng_jent_state) {
+ jitterrng = 0;
+ pr_info("Jitter RNG unusable on current system\n");
+ return 0;
+ }
+ lrng_jent_initialized = true;
+ lrng_pool_add_entropy();
+ pr_debug("Jitter RNG working on current system\n");
+
+ return 0;
+}
+device_initcall(lrng_jent_initialize);
+
+/**
+ * lrng_get_jent() - Get Jitter RNG entropy
+ *
+ * @outbuf: buffer to store entropy
+ * @outbuflen: length of buffer
+ *
+ * Return:
+ * * > 0 on success where value provides the added entropy in bits
+ * * 0 if no fast source was available
+ */
+u32 lrng_get_jent(u8 *outbuf, u32 requested_bits)
+{
+ int ret;
+ u32 ent_bits = lrng_jent_entropylevel(requested_bits);
+ unsigned long flags;
+ static DEFINE_SPINLOCK(lrng_jent_lock);
+
+ spin_lock_irqsave(&lrng_jent_lock, flags);
+
+ if (!ent_bits || !lrng_jent_initialized) {
+ spin_unlock_irqrestore(&lrng_jent_lock, flags);
+ return 0;
+ }
+
+ ret = jent_read_entropy(lrng_jent_state, outbuf, requested_bits >> 3);
+ spin_unlock_irqrestore(&lrng_jent_lock, flags);
+
+ if (ret) {
+ pr_debug("Jitter RNG failed with %d\n", ret);
+ return 0;
+ }
+
+ pr_debug("obtained %u bits of entropy from Jitter RNG noise source\n",
+ ent_bits);
+
+ return ent_bits;
+}
+
+u32 lrng_jent_entropylevel(u32 requested_bits)
+{
+ return lrng_fast_noise_entropylevel((lrng_jent_initialized) ?
+ jitterrng : 0, requested_bits);
+}
--
2.31.1




2021-07-14 11:55:28

by Alexander Lobakin

[permalink] [raw]
Subject: Re: [PATCH v41 00/13] /dev/random - a new approach

Hi,

From: Stephan Müller <[email protected]>
Date: Wed, 14 Jul 2021 07:43:20 +0200

> Hi,
>
> The following patch set provides a different approach to /dev/random which
> is called Linux Random Number Generator (LRNG) to collect entropy within
> the Linux kernel. It provides the same API and ABI and can be used as a
> drop-in replacement. A general overview is given with [6].
>
> The LRNG implements at least all features of the existing /dev/random such as
> NUMA-node-local DRNGs. Patches 1 through 3 provide the code that is feature-
> identical. The following advantages compared to the existing /dev/random
> implementation are present:
>
> * Sole use of crypto for data processing:
>
> - Exclusive use of a hash operation for conditioning entropy data with
> a clear mathematical description as given in [2] section 2.2 -
> non-cryptographic operations like LFSR are not used.
>
> - The LRNG uses only properly defined and implemented cryptographic
> algorithms unlike the use of the SHA-1 transformation in the existing
> /dev/random implementation.
>
> - Hash operations use NUMA-node-local hash instances to benefit large
> parallel systems.
>
> - LRNG uses limited number of data post-processing steps as documented in
> [2] section 2.2 compared to the large variation of different
> post-processing steps in the existing /dev/random implementation that
> have no apparent mathematical description (see [2] section 4.5).
>
> * Performance
>
> - Faster by up to 130% in the critical code path of the interrupt handler
> depending on data collection size configurable at kernel compile time -
> the default is now set such that the highest performance is achieved as
> outlined in [2] section 4.2.
>
> - Configurable data collection sizes to accommodate small environments
> and big environments via CONFIG_LRNG_COLLECTION_SIZE.
>
> - Entropy collection using an almost never contended lock to benefit
> large parallel systems – worst case rate of contention is the number
> of DRNG reseeds, usually the number of potential contentions per 10
> minutes is equal to number of NUMA nodes.
>
> - ChaCha20 DRNG is significantly faster as implemented in the existing
> /dev/random as demonstrated with [2] table 2.
>
> - Faster entropy collection during boot time to reach fully seeded
> level, including on virtual systems or systems with SSDs as outlined
> in [2] section 4.1.
>
> - Faster processing of external data added to LRNG via /dev/random
> or add_hwgenerator_randomness.
>
> * Testing
>
> - Availability of run-time health tests of the raw unconditioned
> noise source to identify degradation of the available entropy as
> documented in [2] section 2.5.4. Such health tests are important
> today due to virtual machine monitors reducing the resolution of
> or disabling the high-resolution timer.
>
> - Heuristic entropy estimation is based on quantitative measurements
> and analysis following SP800-90B and not on coincidental
> underestimation of entropy applied by the existing /dev/random as
> outlined in [4] section 4.4.
>
> - Power-on self tests for critical deterministic components (ChaCha20
> DRNG, software hash implementation, and entropy collection logic)
> not already covered by power-up tests of the kernel crypto API as
> documented in [2] section 2.14.
>
> - Availability of test interfaces for all operational stages of the
> LRNG including boot-time raw entropy event data sampling as outlined
> in [2] section 2.15.
>
> - Fully testable ChaCha20 DRNG via a userspace ChaCha20 DRNG
> implementation [3].
>
> - In case of using the kernel crypto API SHASH hash implementation, it
> is fully testable and tested via the NIST ACVP test framework, for
> example certificates A734, A737, and A738.
>
> - The LRNG offers a test interface to validate the used software hash
> implementation and in particular that the LRNG invokes the hash
> correctly, allowing a NIST ACVP-compliant test cycle - see [2]
> section 2.15.
>
> - Availability of stress testing covering the different code paths for
> data and mechanism (de)allocations and code paths covered with locks.
>
> - Availability of regression tests verifying the different options provided
> with the LRNG.
>
> * Entropy collection
>
> - The LRNG is shipped with test tools allowing the collection of
> raw unconditioned entropy during runtime and boot time available at
> [1].
>
> - Full entropy assessment and description is provided with [2] chapter 3,
> specifically section 3.2.6.
>
> - Guarantee that entropy events are not credited with entropy twice
> (the existing /dev/random implementation credits HID/disk and
> interrupt events with entropy which are a derivative of each other).
>
> * Configurable
>
> - LRNG kernel configuration allows configuration that is functionally
> equivalent to the existing /dev/random. Non-compiled additional code
> is folded into no-ops.
>
> - The following additional functions are compile-time selectable
> independent of each other:
>
> + Enabling of switchable cryptographic implementation support. This
> allows enabling an SP800-90A DRBG.
>
> + Enabling of using Jitter RNG noise source.
>
> + Enabling of noise source health tests.
>
> + Enabling of test interface allowing to enable each test interface
> individually.
>
> + Enabling of the power-up self test.
>
> - At boot-time, the SP800-90B health tests can be enabled as outlined
> in [2] section 2.5.4.
>
> - At boot-time, the entropy rate used to credit the external CPU-based
> noise source and Jitter RNG noise source can be configured including
> setting an entropy rate of zero or full entropy - see [2] sections
> 2.5.2 and 2.5.3.
>
> * Run-time pluggable cryptographic implementations used for all data
> processing steps specified in [2] section 2.2
>
> - The DRNG can be replaced with a different implementation allowing
> any type of DRNG to provide data via the output interfaces. The LRNG
> provides the following types of DRNG implementations:
>
> + ChaCha20-based software implementation that is used per default.
>
> + SP800-90A DRBG using accelerated cryptographic implementations that
> may sleep.
>
> + Any DRNG that is accessible via the kernel crypto API RNG subsystem.
>
> - The hash component can be replaced with any other hash implementation
> provided the implementation does not sleep. The LRNG provides the
> access to the following types of non-sleeping hash implementations:
>
> + SHA-256 software implementation that is used per default. Due to
> kernel build system inconsistencies, the software SHA-1 implementation
> is used if the kernel crypto API is not compiled.
>
> + SHA-512 hash using the fastest hash implementation available via the
> kernel crypto API SHASH subsystem.
>
> * Code structure
>
> - The LRNG source code is available for current upstream Linux kernel
> separate to the existing /dev/random which means that users who are
> conservative can use the unchanged existing /dev/random implementation.
>
> - Back-port patches are available at [5] to apply the LRNG to Linux
> kernel versions of 5.10, 5.8, 5.4, 4.19, 4.14, 4.12, 4.10, and 4.4. Patches
> for other kernel versions are easily derived from the existing ones.
>
> Booting the patch with the kernel command line option
> "dyndbg=file drivers/char/lrng/* +p" generates logs indicating the
> operation of the LRNG. Each log is pre-pended with "lrng".
>
> An entropy analysis is performed on the following systems - details
> are given in [2] appendix C:
>
> * x86 KVM virtualized guest 32 and 64 bit systems
>
> * x86 bare metal
>
> * older and newer ARMv7 system
>
> * ARM64
>
> * POWER7 LE and POWER 8 BE
>
> * IBM Z System mainframe
>
> * old MIPS embedded device
>
> * testing with GCC and Clang
>
> [1] https://www.chronox.de/lrng.html - If the patch is accepted, I would
> be volunteering to convert the documentation into RST format and
> contribute it to the Linux kernel documentation directory.
>
> [2] https://www.chronox.de/lrng/doc/lrng.pdf
>
> [3] https://www.chronox.de/chacha20_drng.html
>
> [4] https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/Studies/LinuxRNG/LinuxRNG_EN_V4_1.pdf
>
> [5] https://github.com/smuellerDD/lrng/tree/master/backports
>
> [6] https://www.chronox.de/lrng/doc/lrng_presentation_v41.pdf
>
> Changes (compared to the previous patch set) - individual patches
> are visible at https://github.com/smuellerDD/lrng/commits/master:
>
> - update seeding threshold when loading DRNG only if min seeded
>
> - insert bootloader entropy directly into the aux pool
>
> - RNDADDTOENTCNT - entropy estmate update of aux pool
>
> - cleanup of initial seeding
>
> - use work queue after initialization only
>
> - add_random_ready_callback ping after full initialization
>
> - zeroize seed buffer
>
> - invoke processing of ready_list only if fully seeded
>
> - invoke invalidate_batched_entropy after initialization
>
> - always fill in the time stamp into seed buffer
>
> - initialize entropy value if insufficient entropy available
>
> - signal end of boot cycle in non-NUMA configuration
>
> - set NUMA node online flag in proper condition
>
> - harden entropy source configuration
>
> - significantly enhance performance of aux pool
>
> - fix LRNG reseed locking
>
> - set LRNG to non-operational for 90C compliance
>
> - fix increment of ChaCha20 nonce
>
> - make switch functions a noop if switching is disabled
>
> CC: Torsten Duwe <[email protected]>
> CC: "Eric W. Biederman" <[email protected]>
> CC: "Alexander E. Patrakov" <[email protected]>
> CC: "Ahmed S. Darwish" <[email protected]>
> CC: "Theodore Y. Ts'o" <[email protected]>
> CC: Willy Tarreau <[email protected]>
> CC: Matthew Garrett <[email protected]>
> CC: Vito Caputo <[email protected]>
> CC: Andreas Dilger <[email protected]>
> CC: Jan Kara <[email protected]>
> CC: Ray Strode <[email protected]>
> CC: William Jon McCann <[email protected]>
> CC: zhangjs <[email protected]>
> CC: Andy Lutomirski <[email protected]>
> CC: Florian Weimer <[email protected]>
> CC: Lennart Poettering <[email protected]>
> CC: Nicolai Stange <[email protected]>
> CC: Eric Biggers <[email protected]>
> CC: Alexander Lobakin <[email protected]>

For the series:

Reviewed-by: Alexander Lobakin <[email protected]>
Tested-by: Alexander Lobakin <[email protected]>

> Tested-by: Marcelo Henrique Cerri <[email protected]>
>
> Stephan Mueller (13):
> Linux Random Number Generator
> LRNG - allocate one DRNG instance per NUMA node
> LRNG - sysctls and /proc interface
> LRNG - add switchable DRNG support
> LRNG - add common generic hash support
> crypto: DRBG - externalize DRBG functions for LRNG
> LRNG - add SP800-90A DRBG extension
> LRNG - add kernel crypto API PRNG extension
> crypto: provide access to a static Jitter RNG state
> LRNG - add Jitter RNG fast noise source
> LRNG - add SP800-90B compliant health tests
> LRNG - add interface for gathering of raw entropy
> LRNG - add power-on and runtime self-tests
>
> MAINTAINERS | 7 +
> crypto/drbg.c | 16 +-
> crypto/jitterentropy-kcapi.c | 3 +-
> crypto/jitterentropy.c | 31 +-
> drivers/char/Kconfig | 2 +
> drivers/char/Makefile | 9 +-
> drivers/char/lrng/Kconfig | 515 +++++++++++++
> drivers/char/lrng/Makefile | 20 +
> drivers/char/lrng/lrng_archrandom.c | 91 +++
> drivers/char/lrng/lrng_aux.c | 136 ++++
> drivers/char/lrng/lrng_chacha20.c | 321 ++++++++
> drivers/char/lrng/lrng_chacha20.h | 29 +
> drivers/char/lrng/lrng_drbg.c | 198 +++++
> drivers/char/lrng/lrng_drng.c | 422 +++++++++++
> drivers/char/lrng/lrng_health.c | 410 ++++++++++
> drivers/char/lrng/lrng_interfaces.c | 648 ++++++++++++++++
> drivers/char/lrng/lrng_internal.h | 425 +++++++++++
> drivers/char/lrng/lrng_jent.c | 90 +++
> drivers/char/lrng/lrng_kcapi.c | 227 ++++++
> drivers/char/lrng/lrng_kcapi_hash.c | 103 +++
> drivers/char/lrng/lrng_kcapi_hash.h | 20 +
> drivers/char/lrng/lrng_numa.c | 122 +++
> drivers/char/lrng/lrng_pool.c | 622 ++++++++++++++++
> drivers/char/lrng/lrng_proc.c | 185 +++++
> drivers/char/lrng/lrng_selftest.c | 351 +++++++++
> drivers/char/lrng/lrng_sw_noise.c | 702 ++++++++++++++++++
> drivers/char/lrng/lrng_sw_noise.h | 71 ++
> drivers/char/lrng/lrng_switch.c | 231 ++++++
> drivers/char/lrng/lrng_testing.c | 689 +++++++++++++++++
> include/crypto/drbg.h | 7 +
> .../crypto/internal}/jitterentropy.h | 3 +
> include/linux/lrng.h | 81 ++
> 32 files changed, 6777 insertions(+), 10 deletions(-)
> create mode 100644 drivers/char/lrng/Kconfig
> create mode 100644 drivers/char/lrng/Makefile
> create mode 100644 drivers/char/lrng/lrng_archrandom.c
> create mode 100644 drivers/char/lrng/lrng_aux.c
> create mode 100644 drivers/char/lrng/lrng_chacha20.c
> create mode 100644 drivers/char/lrng/lrng_chacha20.h
> create mode 100644 drivers/char/lrng/lrng_drbg.c
> create mode 100644 drivers/char/lrng/lrng_drng.c
> create mode 100644 drivers/char/lrng/lrng_health.c
> create mode 100644 drivers/char/lrng/lrng_interfaces.c
> create mode 100644 drivers/char/lrng/lrng_internal.h
> create mode 100644 drivers/char/lrng/lrng_jent.c
> create mode 100644 drivers/char/lrng/lrng_kcapi.c
> create mode 100644 drivers/char/lrng/lrng_kcapi_hash.c
> create mode 100644 drivers/char/lrng/lrng_kcapi_hash.h
> create mode 100644 drivers/char/lrng/lrng_numa.c
> create mode 100644 drivers/char/lrng/lrng_pool.c
> create mode 100644 drivers/char/lrng/lrng_proc.c
> create mode 100644 drivers/char/lrng/lrng_selftest.c
> create mode 100644 drivers/char/lrng/lrng_sw_noise.c
> create mode 100644 drivers/char/lrng/lrng_sw_noise.h
> create mode 100644 drivers/char/lrng/lrng_switch.c
> create mode 100644 drivers/char/lrng/lrng_testing.c
> rename {crypto => include/crypto/internal}/jitterentropy.h (84%)
> create mode 100644 include/linux/lrng.h
>
> --
> 2.31.1

Thanks,
Al