2021-11-21 16:49:50

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v43 00/15] /dev/random - a new approach

Hi,

The following patch set provides a complete production-
ready and yet 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 was presented at the Linux Security Summit 2021. The video of the
presentation can be found at [7].

Patches 1 through 5 provide the solution that is functionality equivalent
with the existing /dev/random implementation. Those patches can be applied
all by themselves and provide a fully functional LRNG. Each subsequent
patch provides a stand-alone additional function.

The LRNG implements at least all features of the existing /dev/random such as
NUMA-node-local DRNGs. 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.

- Block device data path is not instrumented by LRNG which implies that
the LRNG does not add any delays compared to the legacy /dev/random.

- 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
entropy source data of the interrupt entropy source to identify
degradation of the available entropy as documented in [2] section
2.5.2. 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 of the internal interrupt entropy source

- 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.3.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.

+ Selectively enabling of each entropy source and configuring their
entropy rate.

+ Enabling of interrupt entropy source health tests.

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

+ Enabling of the power-up self test.

+ At compile time, the entropy rate used to credit the internal
interrupt entropy source, 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 sections 2.5.1, 2.9.1 and
2.8.1 in [2]. A rate of zero implies that the entropy source still
provides data which is credited with zero bits of entropy.

- At boot-time, the SP800-90B health tests for the internal interrupt
entropy source can be enabled as outlined in [2] section 2.5.2.

- Configurable seeding strategies are provided following different
concepts.

* 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 LTS Linux
kernel versions of 5.10, 5.4, 4.19, 4.14, and 4.4. In addition,
backport patches for the kernel version 5.8, 4.12 and 4.10 are
provided. 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

* RISC-V

* 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_v43.pdf

[7] https://www.youtube.com/watch?v=8DuhbwWkMQw&list=PLbzoR-pLrL6owx0I6v2MSwjxhtsRdtFzG&index=16

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

- Replace '/**' with '/*' in comments

- Jitter RNG ES: if configured to have zero entropy, it still returns data

- CPU ES: if configured to have zero entropy, it still returns data;
allow disabling it with config option - extract CPU ES into its own
patch for better assessment (patch 5)

- IRQ ES: if configured to have zero entropy, it still returns data;
allow disabling it with config option - extract IRQ ES into its own
patch for better assessment (patch 2)

- use hash lock only when LRNG switching support is enabled

- write_wakeup now contains the wakeup threshold in bits and is updated
when the hash is updated

- fix: do not expect user space to provide entropy if it is woken up -
this allows haveged to fully work

- rearrange patch set: patches 01 through 05 are the baseline to get the LRNG
running covering all use cases currently available. Even this base
would provide better functionality than the existing /dev/random
implementation considering the use of SHA-256 and a much faster IRQ
handler.

- fix: ensure that applying the oversampling rate implies a larger or equal
interrupt collection in case no highres timer is present

- fix: PowerISA DARN specifies that it only delivers 0.5 bits of entropy
-> CPU ES takes this into account when configured to trust CPU

- add compression support for CPU entropy sources without full entropy

Stephan Mueller (15):
Linux Random Number Generator
LRNG - IRQ entropy source
LRNG - sysctls and /proc interface
LRNG - allocate one DRNG instance per NUMA node
LRNG - CPU entropy source
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: move Jitter RNG header include dir
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 | 2 +-
drivers/char/Kconfig | 2 +
drivers/char/Makefile | 9 +-
drivers/char/lrng/Kconfig | 589 +++++++++++++
drivers/char/lrng/Makefile | 21 +
drivers/char/lrng/lrng_aux.c | 136 +++
drivers/char/lrng/lrng_chacha20.c | 321 +++++++
drivers/char/lrng/lrng_chacha20.h | 25 +
drivers/char/lrng/lrng_drbg.c | 198 +++++
drivers/char/lrng/lrng_drng.c | 451 ++++++++++
drivers/char/lrng/lrng_es_archrandom.c | 226 +++++
drivers/char/lrng/lrng_es_aux.c | 294 +++++++
drivers/char/lrng/lrng_es_irq.c | 823 ++++++++++++++++++
drivers/char/lrng/lrng_es_irq.h | 71 ++
drivers/char/lrng/lrng_es_jent.c | 97 +++
drivers/char/lrng/lrng_es_mgr.c | 373 ++++++++
drivers/char/lrng/lrng_health.c | 410 +++++++++
drivers/char/lrng/lrng_interfaces.c | 654 ++++++++++++++
drivers/char/lrng/lrng_internal.h | 485 +++++++++++
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_proc.c | 199 +++++
drivers/char/lrng/lrng_selftest.c | 386 ++++++++
drivers/char/lrng/lrng_switch.c | 226 +++++
drivers/char/lrng/lrng_testing.c | 689 +++++++++++++++
include/crypto/drbg.h | 7 +
.../crypto/internal}/jitterentropy.h | 0
include/linux/lrng.h | 81 ++
33 files changed, 7263 insertions(+), 10 deletions(-)
create mode 100644 drivers/char/lrng/Kconfig
create mode 100644 drivers/char/lrng/Makefile
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_es_archrandom.c
create mode 100644 drivers/char/lrng/lrng_es_aux.c
create mode 100644 drivers/char/lrng/lrng_es_irq.c
create mode 100644 drivers/char/lrng/lrng_es_irq.h
create mode 100644 drivers/char/lrng/lrng_es_jent.c
create mode 100644 drivers/char/lrng/lrng_es_mgr.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_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_proc.c
create mode 100644 drivers/char/lrng/lrng_selftest.c
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 (100%)
create mode 100644 include/linux/lrng.h

--
2.31.1






2021-11-21 16:49:51

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v43 02/15] LRNG - IRQ entropy source

The interrupt entropy source hooks into the interrupt handler via the
add_interrupt_randomness function callback. Every interrupt received by
the kernel is also sent to the LRNG for processing.

The IRQ entropy source performs the following processing:

1. Record a time stamp

2. Divide the time stamp by its greatest common divisor to eliminate
fixed least significant bits.

3. Insert the 8 LSB of the result from step 2 into the collection pool.

4. When the collection pool is full, it is hashed into the per-CPU
entropy pool (if continuous compression is enabled) or the latest
time stamps overwrite the oldest entries in the collection pool.

If entropy is requested from the IRQ entropy pool, a message digest over
all per-CPU entropy pool digests is calculated.

The GCD calculation is performed for the first 100 interupt time stamps.
Until the GCD value is calculated, the full 32 bit time stamp is
inserted into the collection pool.

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]>
Reviewed-by: Alexander Lobakin <[email protected]>
Tested-by: Alexander Lobakin <[email protected]>
Mathematical aspects Reviewed-by: "Peter, Matthias" <[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]>
Tested-by: Jirka Hladky <[email protected]>
Reviewed-by: Jirka Hladky <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Kconfig | 162 +++++++
drivers/char/lrng/Makefile | 2 +
drivers/char/lrng/lrng_es_irq.c | 823 ++++++++++++++++++++++++++++++++
drivers/char/lrng/lrng_es_irq.h | 71 +++
4 files changed, 1058 insertions(+)
create mode 100644 drivers/char/lrng/lrng_es_irq.c
create mode 100644 drivers/char/lrng/lrng_es_irq.h

diff --git a/drivers/char/lrng/Kconfig b/drivers/char/lrng/Kconfig
index 655d873480b0..a9ae35d9f648 100644
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -55,4 +55,166 @@ config LRNG_SEED_BUFFER_INIT_ADD_BITS

endmenu # "Specific DRNG seeding strategies"

+menu "Entropy Source Configuration"
+
+comment "Interrupt Entropy Source"
+
+config LRNG_IRQ
+ bool "Enable Interrupt Entropy Source as LRNG Seed Source"
+ default y
+ help
+ The LRNG models an entropy source based on the timing of the
+ occurrence of interrupts. Enable this option to enable this
+ IRQ entropy source.
+
+ The IRQ entropy source is triggered every time an interrupt
+ arrives and thus causes the interrupt handler to execute
+ slightly longer. Disabling the IRQ entropy source implies
+ that the performance penalty on the interrupt handler added
+ by the LRNG is eliminated. Yet, this entropy source is
+ considered to be the internal entropy source of the LRNG.
+ Thus, only disable it if you ensured that other entropy
+ sources are available that supply the LRNG with entropy.
+
+ If you disable the IRQ entropy source, you MUST ensure
+ one or more entropy sources collectively have the
+ capability to deliver sufficient entropy with one invocation
+ at a rate compliant to the security strength of the DRNG
+ (usually 256 bits of entropy). In addition, if those
+ entropy sources do not deliver sufficient entropy during
+ first request, the reseed must be triggered from user
+ space or kernel space when sufficient entropy is considered
+ to be present.
+
+ If unsure, say Y.
+
+choice
+ prompt "Continuous entropy compression boot time setting"
+ default LRNG_CONTINUOUS_COMPRESSION_ENABLED
+ depends on LRNG_IRQ
+ help
+ Select the default behavior of the interrupt entropy source
+ continuous compression operation.
+
+ The Linux RNG collects entropy data during each interrupt.
+ For performance reasons, a amount of entropy data defined by
+ the LRNG entropy collection pool size is concatenated into
+ an array. When that array is filled up, a hash is calculated
+ to compress the entropy. That hash is calculated in
+ interrupt context.
+
+ In case such hash calculation in interrupt context is deemed
+ too time-consuming, the continuous compression operation
+ can be disabled. If disabled, the collection of entropy will
+ not trigger a hash compression operation in interrupt context.
+ The compression happens only when the DRNG is reseeded which is
+ in process context. This implies that old entropy data
+ collected after the last DRNG-reseed is overwritten with newer
+ entropy data once the collection pool is full instead of
+ retaining its entropy with the compression operation.
+
+ config LRNG_CONTINUOUS_COMPRESSION_ENABLED
+ bool "Enable continuous compression (default)"
+
+ config LRNG_CONTINUOUS_COMPRESSION_DISABLED
+ bool "Disable continuous compression"
+endchoice
+
+config LRNG_ENABLE_CONTINUOUS_COMPRESSION
+ bool
+ default y if LRNG_CONTINUOUS_COMPRESSION_ENABLED
+ default n if LRNG_CONTINUOUS_COMPRESSION_DISABLED
+
+config LRNG_SWITCHABLE_CONTINUOUS_COMPRESSION
+ bool "Runtime-switchable continuous entropy compression"
+ depends on LRNG_IRQ
+ help
+ Per default, the interrupt entropy source continuous
+ compression operation behavior is hard-wired into the kernel.
+ Enable this option to allow it to be configurable at boot time.
+
+ To modify the default behavior of the continuous
+ compression operation, use the kernel command line option
+ of lrng_sw_noise.lrng_pcpu_continuous_compression.
+
+ If unsure, say N.
+
+choice
+ prompt "LRNG Entropy Collection Pool Size"
+ default LRNG_COLLECTION_SIZE_1024
+ depends on LRNG_IRQ
+ help
+ Select the size of the LRNG entropy collection pool
+ storing data for the interrupt entropy source without
+ performing a compression operation. The larger the
+ collection size is, the faster the average interrupt
+ handling will be. The collection size represents the
+ number of bytes of the per-CPU memory used to batch
+ up entropy event data.
+
+ The default value is good for regular operations. Choose
+ larger sizes for servers that have no memory limitations.
+ If runtime memory is precious, choose a smaller size.
+
+ The collection size is unrelated to the entropy rate
+ or the amount of entropy the LRNG can process.
+
+ config LRNG_COLLECTION_SIZE_32
+ depends on LRNG_CONTINUOUS_COMPRESSION_ENABLED
+ depends on !LRNG_SWITCHABLE_CONTINUOUS_COMPRESSION
+ depends on !LRNG_OVERSAMPLE_ENTROPY_SOURCES
+ bool "32 interrupt events"
+
+ config LRNG_COLLECTION_SIZE_256
+ depends on !LRNG_OVERSAMPLE_ENTROPY_SOURCES
+ bool "256 interrupt events"
+
+ config LRNG_COLLECTION_SIZE_512
+ bool "512 interrupt events"
+
+ config LRNG_COLLECTION_SIZE_1024
+ bool "1024 interrupt events (default)"
+
+ config LRNG_COLLECTION_SIZE_2048
+ bool "2048 interrupt events"
+
+ config LRNG_COLLECTION_SIZE_4096
+ bool "4096 interrupt events"
+
+ config LRNG_COLLECTION_SIZE_8192
+ bool "8192 interrupt events"
+
+endchoice
+
+config LRNG_COLLECTION_SIZE
+ int
+ default 32 if LRNG_COLLECTION_SIZE_32
+ default 256 if LRNG_COLLECTION_SIZE_256
+ default 512 if LRNG_COLLECTION_SIZE_512
+ default 1024 if LRNG_COLLECTION_SIZE_1024
+ default 2048 if LRNG_COLLECTION_SIZE_2048
+ default 4096 if LRNG_COLLECTION_SIZE_4096
+ default 8192 if LRNG_COLLECTION_SIZE_8192
+
+config LRNG_IRQ_ENTROPY_RATE
+ int "Interrupt Entropy Source Entropy Rate"
+ depends on LRNG_IRQ
+ range 256 4294967295
+ default 256
+ help
+ The LRNG will collect the configured number of interrupts to
+ obtain 256 bits of entropy. This value can be set to any between
+ 256 and 4294967295. The LRNG guarantees that this value is not
+ lower than 256. This lower limit implies that one interrupt event
+ is credited with one bit of entropy. This value is subject to the
+ increase by the oversampling factor, if no high-resolution timer
+ is found.
+
+ In order to effectively disable the interrupt entropy source,
+ the option has to be set to 4294967295. In this case, the
+ interrupt entropy source will still deliver data but without
+ being credited with entropy.
+
+endmenu # "Entropy Source Configuration"
+
endif # LRNG
diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile
index 6f4603f897cd..d321d6d21a44 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -6,3 +6,5 @@
obj-y += lrng_es_mgr.o lrng_aux.o \
lrng_drng.o lrng_chacha20.o \
lrng_interfaces.o lrng_es_aux.o
+
+obj-$(CONFIG_LRNG_IRQ) += lrng_es_irq.o
diff --git a/drivers/char/lrng/lrng_es_irq.c b/drivers/char/lrng/lrng_es_irq.c
new file mode 100644
index 000000000000..82a73b0426fb
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_irq.c
@@ -0,0 +1,823 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG Slow Entropy Source: Interrupt data collection
+ *
+ * Copyright (C) 2016 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <asm/irq_regs.h>
+#include <asm/ptrace.h>
+#include <crypto/hash.h>
+#include <linux/gcd.h>
+#include <linux/lrng.h>
+#include <linux/random.h>
+
+#include "lrng_internal.h"
+#include "lrng_es_irq.h"
+
+/*
+ * Number of interrupts to be recorded to assume that DRNG security strength
+ * bits of entropy are received.
+ * Note: a value below the DRNG security strength should not be defined as this
+ * may imply the DRNG can never be fully seeded in case other noise
+ * sources are unavailable.
+ */
+#define LRNG_IRQ_ENTROPY_BITS CONFIG_LRNG_IRQ_ENTROPY_RATE
+
+
+/* Number of interrupts required for LRNG_DRNG_SECURITY_STRENGTH_BITS entropy */
+static u32 lrng_irq_entropy_bits = LRNG_IRQ_ENTROPY_BITS;
+/* Is high-resolution timer present? */
+static bool lrng_irq_highres_timer = false;
+
+static u32 irq_entropy __read_mostly = LRNG_IRQ_ENTROPY_BITS;
+#ifdef CONFIG_LRNG_RUNTIME_ES_CONFIG
+module_param(irq_entropy, uint, 0444);
+MODULE_PARM_DESC(irq_entropy,
+ "How many interrupts must be collected for obtaining 256 bits of entropy\n");
+#endif
+
+/* Per-CPU array holding concatenated entropy events */
+static DEFINE_PER_CPU(u32 [LRNG_DATA_ARRAY_SIZE], lrng_pcpu_array)
+ __aligned(LRNG_KCAPI_ALIGN);
+static DEFINE_PER_CPU(u32, lrng_pcpu_array_ptr) = 0;
+static DEFINE_PER_CPU(atomic_t, lrng_pcpu_array_irqs) = ATOMIC_INIT(0);
+
+/*
+ * The entropy collection is performed by executing the following steps:
+ * 1. fill up the per-CPU array holding the time stamps
+ * 2. once the per-CPU array is full, a compression of the data into
+ * the entropy pool is performed - this happens in interrupt context
+ *
+ * If step 2 is not desired in interrupt context, the following boolean
+ * needs to be set to false. This implies that old entropy data in the
+ * per-CPU array collected since the last DRNG reseed is overwritten with
+ * new entropy data instead of retaining the entropy with the compression
+ * operation.
+ *
+ * Impact on entropy:
+ *
+ * If continuous compression is enabled, the maximum entropy that is collected
+ * per CPU between DRNG reseeds is equal to the digest size of the used hash.
+ *
+ * If continuous compression is disabled, the maximum number of entropy events
+ * that can be collected per CPU is equal to LRNG_DATA_ARRAY_SIZE. This amount
+ * of events is converted into an entropy statement which then represents the
+ * maximum amount of entropy collectible per CPU between DRNG reseeds.
+ */
+static bool lrng_pcpu_continuous_compression __read_mostly =
+ IS_ENABLED(CONFIG_LRNG_ENABLE_CONTINUOUS_COMPRESSION);
+
+#ifdef CONFIG_LRNG_SWITCHABLE_CONTINUOUS_COMPRESSION
+module_param(lrng_pcpu_continuous_compression, bool, 0444);
+MODULE_PARM_DESC(lrng_pcpu_continuous_compression,
+ "Perform entropy compression if per-CPU entropy data array is full\n");
+#endif
+
+/*
+ * Per-CPU entropy pool with compressed entropy event
+ *
+ * The per-CPU entropy pool is defined as the hash state. New data is simply
+ * inserted into the entropy pool by performing a hash update operation.
+ * To read the entropy pool, a hash final must be invoked. However, before
+ * the entropy pool is released again after a hash final, the hash init must
+ * be performed.
+ */
+static DEFINE_PER_CPU(u8 [LRNG_POOL_SIZE], lrng_pcpu_pool)
+ __aligned(LRNG_KCAPI_ALIGN);
+/*
+ * Lock to allow other CPUs to read the pool - as this is only done during
+ * reseed which is infrequent, this lock is hardly contended.
+ */
+static DEFINE_PER_CPU(spinlock_t, lrng_pcpu_lock);
+static DEFINE_PER_CPU(bool, lrng_pcpu_lock_init) = false;
+
+/* Number of time stamps analyzed to calculate a GCD */
+#define LRNG_GCD_WINDOW_SIZE 100
+static u32 lrng_gcd_history[LRNG_GCD_WINDOW_SIZE];
+static atomic_t lrng_gcd_history_ptr = ATOMIC_INIT(-1);
+
+/* The common divisor for all timestamps */
+static u32 lrng_gcd_timer = 0;
+
+static inline bool lrng_gcd_tested(void)
+{
+ return (lrng_gcd_timer != 0);
+}
+
+/* Set the GCD for use in IRQ ES - if 0, the GCD calculation is restarted. */
+static inline void _lrng_gcd_set(u32 running_gcd)
+{
+ lrng_gcd_timer = running_gcd;
+ mb();
+}
+
+static void lrng_gcd_set(u32 running_gcd)
+{
+ if (!lrng_gcd_tested()) {
+ _lrng_gcd_set(running_gcd);
+ pr_debug("Setting GCD to %u\n", running_gcd);
+ }
+}
+
+u32 lrng_gcd_analyze(u32 *history, size_t nelem)
+{
+ u32 running_gcd = 0;
+ size_t i;
+
+ /* Now perform the analysis on the accumulated time data. */
+ for (i = 0; i < nelem; i++) {
+ /*
+ * NOTE: this would be the place to add more analysis on the
+ * appropriateness of the timer like checking the presence
+ * of sufficient variations in the timer.
+ */
+
+ /*
+ * This calculates the gcd of all the time values. that is
+ * gcd(time_1, time_2, ..., time_nelem)
+ *
+ * Some timers increment by a fixed (non-1) amount each step.
+ * This code checks for such increments, and allows the library
+ * to output the number of such changes have occurred.
+ */
+ running_gcd = (u32)gcd(history[i], running_gcd);
+
+ /* Zeroize data */
+ history[i] = 0;
+ }
+
+ return running_gcd;
+}
+
+static void lrng_gcd_add_value(u32 time)
+{
+ u32 ptr = (u32)atomic_inc_return_relaxed(&lrng_gcd_history_ptr);
+
+ if (ptr < LRNG_GCD_WINDOW_SIZE) {
+ lrng_gcd_history[ptr] = time;
+ } else if (ptr == LRNG_GCD_WINDOW_SIZE) {
+ u32 gcd = lrng_gcd_analyze(lrng_gcd_history,
+ LRNG_GCD_WINDOW_SIZE);
+
+ if (!gcd)
+ gcd = 1;
+
+ /*
+ * Ensure that we have variations in the time stamp below the
+ * given value. This is just a safety measure to prevent the GCD
+ * becoming too large.
+ */
+ if (gcd >= 1000) {
+ pr_warn("calculated GCD is larger than expected: %u\n",
+ gcd);
+ gcd = 1000;
+ }
+
+ /* Adjust all deltas by the observed (small) common factor. */
+ lrng_gcd_set(gcd);
+ atomic_set(&lrng_gcd_history_ptr, 0);
+ }
+}
+
+/* Return boolean whether LRNG identified presence of high-resolution timer */
+static bool lrng_pool_highres_timer(void)
+{
+ return lrng_irq_highres_timer;
+}
+
+/* Convert entropy in bits into number of IRQs with the same entropy content. */
+static inline u32 lrng_entropy_to_data(u32 entropy_bits)
+{
+ return ((entropy_bits * lrng_irq_entropy_bits) /
+ LRNG_DRNG_SECURITY_STRENGTH_BITS);
+}
+
+/* Convert number of IRQs into entropy value. */
+static inline u32 lrng_data_to_entropy(u32 irqnum)
+{
+ return ((irqnum * LRNG_DRNG_SECURITY_STRENGTH_BITS) /
+ lrng_irq_entropy_bits);
+}
+
+static inline bool lrng_pcpu_pool_online(int cpu)
+{
+ return per_cpu(lrng_pcpu_lock_init, cpu);
+}
+
+static void lrng_pcpu_check_compression_state(void)
+{
+ /* One pool must hold sufficient entropy for disabled compression */
+ if (!lrng_pcpu_continuous_compression) {
+ u32 max_ent = min_t(u32, lrng_get_digestsize(),
+ lrng_data_to_entropy(LRNG_DATA_NUM_VALUES));
+ if (max_ent < lrng_security_strength()) {
+ pr_warn("Force continuous compression operation to ensure LRNG can hold enough entropy\n");
+ lrng_pcpu_continuous_compression = true;
+ }
+ }
+}
+
+static int __init lrng_init_time_source(void)
+{
+ /* Set a minimum number of interrupts that must be collected */
+ irq_entropy = max_t(u32, LRNG_IRQ_ENTROPY_BITS, irq_entropy);
+
+ if ((random_get_entropy() & LRNG_DATA_SLOTSIZE_MASK) ||
+ (random_get_entropy() & LRNG_DATA_SLOTSIZE_MASK)) {
+ /*
+ * As the highres timer is identified here, previous interrupts
+ * obtained during boot time are treated like a lowres-timer
+ * would have been present.
+ */
+ lrng_irq_highres_timer = true;
+ lrng_irq_entropy_bits = irq_entropy;
+ } else {
+ u32 new_entropy = irq_entropy * LRNG_IRQ_OVERSAMPLING_FACTOR;
+
+ lrng_health_disable();
+ lrng_irq_highres_timer = false;
+ lrng_irq_entropy_bits = (irq_entropy < new_entropy) ?
+ new_entropy : irq_entropy;
+ pr_warn("operating without high-resolution timer and applying IRQ oversampling factor %u\n",
+ LRNG_IRQ_OVERSAMPLING_FACTOR);
+ lrng_pcpu_check_compression_state();
+ }
+ mb();
+
+ return 0;
+}
+core_initcall(lrng_init_time_source);
+
+/*
+ * Reset all per-CPU pools - reset entropy estimator but leave the pool data
+ * that may or may not have entropy unchanged.
+ */
+void lrng_pcpu_reset(void)
+{
+ int cpu;
+
+ /* Trigger GCD calculation anew. */
+ _lrng_gcd_set(0);
+
+ for_each_online_cpu(cpu)
+ atomic_set(per_cpu_ptr(&lrng_pcpu_array_irqs, cpu), 0);
+}
+
+u32 lrng_pcpu_avail_pool_size(void)
+{
+ u32 max_size = 0, max_pool = lrng_get_digestsize();
+ int cpu;
+
+ if (!lrng_pcpu_continuous_compression)
+ max_pool = min_t(u32, max_pool, LRNG_DATA_NUM_VALUES);
+
+ for_each_online_cpu(cpu) {
+ if (lrng_pcpu_pool_online(cpu))
+ max_size += max_pool;
+ }
+
+ return max_size;
+}
+
+/* Return entropy of unused IRQs present in all per-CPU pools. */
+u32 lrng_pcpu_avail_entropy(void)
+{
+ u32 digestsize_irqs, irq = 0;
+ int cpu;
+
+ /* Obtain the cap of maximum numbers of IRQs we count */
+ digestsize_irqs = lrng_entropy_to_data(lrng_get_digestsize());
+ if (!lrng_pcpu_continuous_compression) {
+ /* Cap to max. number of IRQs the array can hold */
+ digestsize_irqs = min_t(u32, digestsize_irqs,
+ LRNG_DATA_NUM_VALUES);
+ }
+
+ for_each_online_cpu(cpu) {
+ if (!lrng_pcpu_pool_online(cpu))
+ continue;
+ irq += min_t(u32, digestsize_irqs,
+ atomic_read_u32(per_cpu_ptr(&lrng_pcpu_array_irqs,
+ cpu)));
+ }
+
+ /* Consider oversampling rate */
+ return lrng_reduce_by_osr(lrng_data_to_entropy(irq));
+}
+
+/*
+ * Trigger a switch of the hash implementation for the per-CPU pool.
+ *
+ * For each per-CPU pool, obtain the message digest with the old hash
+ * implementation, initialize the per-CPU pool again with the new hash
+ * implementation and inject the message digest into the new state.
+ *
+ * Assumption: the caller must guarantee that the new_cb is available during the
+ * entire operation (e.g. it must hold the lock against pointer updating).
+ */
+int lrng_pcpu_switch_hash(int node,
+ const struct lrng_crypto_cb *new_cb, void *new_hash,
+ const struct lrng_crypto_cb *old_cb)
+{
+ u8 digest[LRNG_MAX_DIGESTSIZE];
+ u32 digestsize_irqs, found_irqs;
+ int ret = 0, cpu;
+
+ if (!IS_ENABLED(CONFIG_LRNG_DRNG_SWITCH))
+ return -EOPNOTSUPP;
+
+ for_each_online_cpu(cpu) {
+ struct shash_desc *pcpu_shash;
+
+ /*
+ * Only switch the per-CPU pools for the current node because
+ * the crypto_cb only applies NUMA-node-wide.
+ */
+ if (cpu_to_node(cpu) != node || !lrng_pcpu_pool_online(cpu))
+ continue;
+
+ pcpu_shash = (struct shash_desc *)per_cpu_ptr(lrng_pcpu_pool,
+ cpu);
+
+ digestsize_irqs = old_cb->lrng_hash_digestsize(pcpu_shash);
+ digestsize_irqs = lrng_entropy_to_data(digestsize_irqs << 3);
+
+ if (pcpu_shash->tfm == new_hash)
+ continue;
+
+ /* Get the per-CPU pool hash with old digest ... */
+ ret = old_cb->lrng_hash_final(pcpu_shash, digest) ?:
+ /* ... re-initialize the hash with the new digest ... */
+ new_cb->lrng_hash_init(pcpu_shash, new_hash) ?:
+ /*
+ * ... feed the old hash into the new state. We may feed
+ * uninitialized memory into the new state, but this is
+ * considered no issue and even good as we have some more
+ * uncertainty here.
+ */
+ new_cb->lrng_hash_update(pcpu_shash, digest,
+ sizeof(digest));
+ if (ret)
+ goto out;
+
+ /*
+ * In case the new digest is larger than the old one, cap
+ * the available entropy to the old message digest used to
+ * process the existing data.
+ */
+ found_irqs = atomic_xchg_relaxed(
+ per_cpu_ptr(&lrng_pcpu_array_irqs, cpu), 0);
+ found_irqs = min_t(u32, found_irqs, digestsize_irqs);
+ atomic_add_return_relaxed(found_irqs,
+ per_cpu_ptr(&lrng_pcpu_array_irqs, cpu));
+
+ pr_debug("Re-initialize per-CPU entropy pool for CPU %d on NUMA node %d with hash %s\n",
+ cpu, node, new_cb->lrng_hash_name());
+ }
+
+out:
+ memzero_explicit(digest, sizeof(digest));
+ return ret;
+}
+
+/*
+ * When reading the per-CPU message digest, make sure we use the crypto
+ * callbacks defined for the NUMA node the per-CPU pool is defined for because
+ * the LRNG crypto switch support is only atomic per NUMA node.
+ */
+static inline u32
+lrng_pcpu_pool_hash_one(const struct lrng_crypto_cb *pcpu_crypto_cb,
+ void *pcpu_hash, int cpu, u8 *digest, u32 *digestsize)
+{
+ struct shash_desc *pcpu_shash =
+ (struct shash_desc *)per_cpu_ptr(lrng_pcpu_pool, cpu);
+ spinlock_t *lock = per_cpu_ptr(&lrng_pcpu_lock, cpu);
+ unsigned long flags;
+ u32 digestsize_irqs, found_irqs;
+
+ /* Lock guarding against reading / writing to per-CPU pool */
+ spin_lock_irqsave(lock, flags);
+
+ *digestsize = pcpu_crypto_cb->lrng_hash_digestsize(pcpu_hash);
+ digestsize_irqs = lrng_entropy_to_data(*digestsize << 3);
+
+ /* Obtain entropy statement like for the entropy pool */
+ found_irqs = atomic_xchg_relaxed(
+ per_cpu_ptr(&lrng_pcpu_array_irqs, cpu), 0);
+ /* Cap to maximum amount of data we can hold in hash */
+ found_irqs = min_t(u32, found_irqs, digestsize_irqs);
+
+ /* Cap to maximum amount of data we can hold in array */
+ if (!lrng_pcpu_continuous_compression)
+ found_irqs = min_t(u32, found_irqs, LRNG_DATA_NUM_VALUES);
+
+ /* Store all not-yet compressed data in data array into hash, ... */
+ if (pcpu_crypto_cb->lrng_hash_update(pcpu_shash,
+ (u8 *)per_cpu_ptr(lrng_pcpu_array, cpu),
+ LRNG_DATA_ARRAY_SIZE * sizeof(u32)) ?:
+ /* ... get the per-CPU pool digest, ... */
+ pcpu_crypto_cb->lrng_hash_final(pcpu_shash, digest) ?:
+ /* ... re-initialize the hash, ... */
+ pcpu_crypto_cb->lrng_hash_init(pcpu_shash, pcpu_hash) ?:
+ /* ... feed the old hash into the new state. */
+ pcpu_crypto_cb->lrng_hash_update(pcpu_shash, digest, *digestsize))
+ found_irqs = 0;
+
+ spin_unlock_irqrestore(lock, flags);
+ return found_irqs;
+}
+
+/*
+ * Hash all per-CPU pools and return the digest to be used as seed data for
+ * seeding a DRNG. The caller must guarantee backtracking resistance.
+ * The function will only copy as much data as entropy is available into the
+ * caller-provided output buffer.
+ *
+ * This function handles the translation from the number of received interrupts
+ * into an entropy statement. The conversion depends on LRNG_IRQ_ENTROPY_BITS
+ * which defines how many interrupts must be received to obtain 256 bits of
+ * entropy. With this value, the function lrng_data_to_entropy converts a given
+ * data size (received interrupts, requested amount of data, etc.) into an
+ * entropy statement. lrng_entropy_to_data does the reverse.
+ *
+ * @outbuf: buffer to store data in with size requested_bits
+ * @requested_bits: Requested amount of entropy
+ * @fully_seeded: indicator whether LRNG is fully seeded
+ * @return: amount of entropy in outbuf in bits.
+ */
+u32 lrng_pcpu_pool_hash(u8 *outbuf, u32 requested_bits, bool fully_seeded)
+{
+ SHASH_DESC_ON_STACK(shash, NULL);
+ const struct lrng_crypto_cb *crypto_cb;
+ struct lrng_drng **lrng_drng = lrng_drng_instances();
+ struct lrng_drng *drng = lrng_drng_init_instance();
+ u8 digest[LRNG_MAX_DIGESTSIZE];
+ unsigned long flags, flags2;
+ u32 found_irqs, collected_irqs = 0, collected_ent_bits, requested_irqs,
+ returned_ent_bits;
+ int ret, cpu;
+ void *hash;
+
+ /* Lock guarding replacement of per-NUMA hash */
+ lrng_hash_lock(drng, &flags);
+
+ crypto_cb = drng->crypto_cb;
+ hash = drng->hash;
+
+ /* The hash state of filled with all per-CPU pool hashes. */
+ ret = crypto_cb->lrng_hash_init(shash, hash);
+ if (ret)
+ goto err;
+
+ requested_irqs = lrng_entropy_to_data(requested_bits +
+ lrng_compress_osr());
+
+ /*
+ * Harvest entropy from each per-CPU hash state - even though we may
+ * have collected sufficient entropy, we will hash all per-CPU pools.
+ */
+ for_each_online_cpu(cpu) {
+ struct lrng_drng *pcpu_drng = drng;
+ u32 digestsize, pcpu_unused_irqs = 0;
+ int node = cpu_to_node(cpu);
+
+ /* If pool is not online, then no entropy is present. */
+ if (!lrng_pcpu_pool_online(cpu))
+ continue;
+
+ if (lrng_drng && lrng_drng[node])
+ pcpu_drng = lrng_drng[node];
+
+ if (pcpu_drng == drng) {
+ found_irqs = lrng_pcpu_pool_hash_one(crypto_cb, hash,
+ cpu, digest,
+ &digestsize);
+ } else {
+ lrng_hash_lock(pcpu_drng, &flags2);
+ found_irqs =
+ lrng_pcpu_pool_hash_one(pcpu_drng->crypto_cb,
+ pcpu_drng->hash, cpu,
+ digest, &digestsize);
+ lrng_hash_unlock(pcpu_drng, flags2);
+ }
+
+ /* Inject the digest into the state of all per-CPU pools */
+ ret = crypto_cb->lrng_hash_update(shash, digest, digestsize);
+ if (ret)
+ goto err;
+
+ collected_irqs += found_irqs;
+ if (collected_irqs > requested_irqs) {
+ pcpu_unused_irqs = collected_irqs - requested_irqs;
+ atomic_add_return_relaxed(pcpu_unused_irqs,
+ per_cpu_ptr(&lrng_pcpu_array_irqs, cpu));
+ collected_irqs = requested_irqs;
+ }
+ pr_debug("%u interrupts used from entropy pool of CPU %d, %u interrupts remain unused\n",
+ found_irqs - pcpu_unused_irqs, cpu, pcpu_unused_irqs);
+ }
+
+ ret = crypto_cb->lrng_hash_final(shash, digest);
+ if (ret)
+ goto err;
+
+ collected_ent_bits = lrng_data_to_entropy(collected_irqs);
+ /* Cap to maximum entropy that can ever be generated with given hash */
+ collected_ent_bits = min_t(u32, collected_ent_bits,
+ crypto_cb->lrng_hash_digestsize(hash) << 3);
+ /* Apply oversampling: discount requested oversampling rate */
+ returned_ent_bits = lrng_reduce_by_osr(collected_ent_bits);
+
+ pr_debug("obtained %u bits by collecting %u bits of entropy from entropy pool noise source\n",
+ returned_ent_bits, collected_ent_bits);
+
+ /*
+ * Truncate to available entropy as implicitly allowed by SP800-90B
+ * section 3.1.5.1.1 table 1 which awards truncated hashes full
+ * entropy.
+ *
+ * During boot time, we read requested_bits data with
+ * returned_ent_bits entropy. In case our conservative entropy
+ * estimate underestimates the available entropy we can transport as
+ * much available entropy as possible.
+ */
+ memcpy(outbuf, digest, fully_seeded ? returned_ent_bits >> 3 :
+ requested_bits >> 3);
+
+out:
+ crypto_cb->lrng_hash_desc_zero(shash);
+ lrng_hash_unlock(drng, flags);
+ memzero_explicit(digest, sizeof(digest));
+ return returned_ent_bits;
+
+err:
+ returned_ent_bits = 0;
+ goto out;
+}
+
+/* Compress the lrng_pcpu_array array into lrng_pcpu_pool */
+static inline void lrng_pcpu_array_compress(void)
+{
+ struct shash_desc *shash =
+ (struct shash_desc *)this_cpu_ptr(lrng_pcpu_pool);
+ struct lrng_drng **lrng_drng = lrng_drng_instances();
+ struct lrng_drng *drng = lrng_drng_init_instance();
+ const struct lrng_crypto_cb *crypto_cb;
+ spinlock_t *lock = this_cpu_ptr(&lrng_pcpu_lock);
+ unsigned long flags, flags2;
+ int node = numa_node_id();
+ void *hash;
+ bool init = false;
+
+ /* Get NUMA-node local hash instance */
+ if (lrng_drng && lrng_drng[node])
+ drng = lrng_drng[node];
+
+ lrng_hash_lock(drng, &flags);
+ crypto_cb = drng->crypto_cb;
+ hash = drng->hash;
+
+ if (unlikely(!this_cpu_read(lrng_pcpu_lock_init))) {
+ init = true;
+ spin_lock_init(lock);
+ this_cpu_write(lrng_pcpu_lock_init, true);
+ pr_debug("Initializing per-CPU entropy pool for CPU %d on NUMA node %d with hash %s\n",
+ raw_smp_processor_id(), node,
+ crypto_cb->lrng_hash_name());
+ }
+
+ spin_lock_irqsave(lock, flags2);
+
+ if (unlikely(init) && crypto_cb->lrng_hash_init(shash, hash)) {
+ this_cpu_write(lrng_pcpu_lock_init, false);
+ pr_warn("Initialization of hash failed\n");
+ } else if (lrng_pcpu_continuous_compression) {
+ /* Add entire per-CPU data array content into entropy pool. */
+ if (crypto_cb->lrng_hash_update(shash,
+ (u8 *)this_cpu_ptr(lrng_pcpu_array),
+ LRNG_DATA_ARRAY_SIZE * sizeof(u32)))
+ pr_warn_ratelimited("Hashing of entropy data failed\n");
+ }
+
+ spin_unlock_irqrestore(lock, flags2);
+ lrng_hash_unlock(drng, flags);
+}
+
+/* Compress data array into hash */
+static inline void lrng_pcpu_array_to_hash(u32 ptr)
+{
+ u32 *array = this_cpu_ptr(lrng_pcpu_array);
+
+ /*
+ * During boot time the hash operation is triggered more often than
+ * during regular operation.
+ */
+ if (unlikely(!lrng_state_fully_seeded())) {
+ if ((ptr & 31) && (ptr < LRNG_DATA_WORD_MASK))
+ return;
+ } else if (ptr < LRNG_DATA_WORD_MASK) {
+ return;
+ }
+
+ if (lrng_raw_array_entropy_store(*array)) {
+ u32 i;
+
+ /*
+ * If we fed even a part of the array to external analysis, we
+ * mark that the entire array and the per-CPU pool to have no
+ * entropy. This is due to the non-IID property of the data as
+ * we do not fully know whether the existing dependencies
+ * diminish the entropy beyond to what we expect it has.
+ */
+ atomic_set(this_cpu_ptr(&lrng_pcpu_array_irqs), 0);
+
+ for (i = 1; i < LRNG_DATA_ARRAY_SIZE; i++)
+ lrng_raw_array_entropy_store(*(array + i));
+ } else {
+ lrng_pcpu_array_compress();
+ /* Ping pool handler about received entropy */
+ lrng_pool_add_entropy();
+ }
+}
+
+/*
+ * Concatenate full 32 bit word at the end of time array even when current
+ * ptr is not aligned to sizeof(data).
+ */
+static inline void _lrng_pcpu_array_add_u32(u32 data)
+{
+ /* Increment pointer by number of slots taken for input value */
+ u32 pre_ptr, mask, ptr = this_cpu_add_return(lrng_pcpu_array_ptr,
+ LRNG_DATA_SLOTS_PER_UINT);
+ unsigned int pre_array;
+
+ /*
+ * This function injects a unit into the array - guarantee that
+ * array unit size is equal to data type of input data.
+ */
+ BUILD_BUG_ON(LRNG_DATA_ARRAY_MEMBER_BITS != (sizeof(data) << 3));
+
+ /*
+ * The following logic requires at least two units holding
+ * the data as otherwise the pointer would immediately wrap when
+ * injection an u32 word.
+ */
+ BUILD_BUG_ON(LRNG_DATA_NUM_VALUES <= LRNG_DATA_SLOTS_PER_UINT);
+
+ 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 */
+ this_cpu_and(lrng_pcpu_array[pre_array], ~(0xffffffff & ~mask));
+ this_cpu_or(lrng_pcpu_array[pre_array], data & ~mask);
+
+ /* Invoke compression as we just filled data array completely */
+ if (unlikely(pre_ptr > ptr))
+ lrng_pcpu_array_to_hash(LRNG_DATA_WORD_MASK);
+
+ /* LSB of data go into current unit */
+ this_cpu_write(lrng_pcpu_array[lrng_data_idx2array(ptr)],
+ data & mask);
+
+ if (likely(pre_ptr <= ptr))
+ lrng_pcpu_array_to_hash(ptr);
+}
+
+/* Concatenate a 32-bit word at the end of the per-CPU array */
+void lrng_pcpu_array_add_u32(u32 data)
+{
+ /*
+ * Disregard entropy-less data without continuous compression to
+ * avoid it overwriting data with entropy when array ptr wraps.
+ */
+ if (lrng_pcpu_continuous_compression)
+ _lrng_pcpu_array_add_u32(data);
+}
+
+/* Concatenate data of max LRNG_DATA_SLOTSIZE_MASK at the end of time array */
+static inline void lrng_pcpu_array_add_slot(u32 data)
+{
+ /* Get slot */
+ u32 ptr = this_cpu_inc_return(lrng_pcpu_array_ptr) &
+ LRNG_DATA_WORD_MASK;
+ unsigned int array = lrng_data_idx2array(ptr);
+ unsigned int slot = lrng_data_idx2slot(ptr);
+
+ BUILD_BUG_ON(LRNG_DATA_ARRAY_MEMBER_BITS % LRNG_DATA_SLOTSIZE_BITS);
+ /* Ensure consistency of values */
+ BUILD_BUG_ON(LRNG_DATA_ARRAY_MEMBER_BITS !=
+ sizeof(lrng_pcpu_array[0]) << 3);
+
+ /* zeroization of slot to ensure the following OR adds the data */
+ this_cpu_and(lrng_pcpu_array[array],
+ ~(lrng_data_slot_val(0xffffffff & LRNG_DATA_SLOTSIZE_MASK,
+ slot)));
+ /* Store data into slot */
+ this_cpu_or(lrng_pcpu_array[array], lrng_data_slot_val(data, slot));
+
+ lrng_pcpu_array_to_hash(ptr);
+}
+
+static inline void
+lrng_time_process_common(u32 time, void(*add_time)(u32 data))
+{
+ enum lrng_health_res health_test;
+
+ if (lrng_raw_hires_entropy_store(time))
+ return;
+
+ health_test = lrng_health_test(time);
+ if (health_test > lrng_health_fail_use)
+ return;
+
+ if (health_test == lrng_health_pass)
+ atomic_inc_return(this_cpu_ptr(&lrng_pcpu_array_irqs));
+
+ add_time(time);
+}
+
+/*
+ * Batching up of entropy in per-CPU array before injecting into entropy pool.
+ */
+static inline void lrng_time_process(void)
+{
+ u32 now_time = random_get_entropy();
+
+ if (unlikely(!lrng_gcd_tested())) {
+ /* When GCD is unknown, we process the full time stamp */
+ lrng_time_process_common(now_time, _lrng_pcpu_array_add_u32);
+ lrng_gcd_add_value(now_time);
+ } else {
+ /* GCD is known and applied */
+ lrng_time_process_common((now_time / lrng_gcd_timer) &
+ LRNG_DATA_SLOTSIZE_MASK,
+ lrng_pcpu_array_add_slot);
+ }
+
+ lrng_perf_time(now_time);
+}
+
+/* Hot code path - Callback for interrupt handler */
+void add_interrupt_randomness(int irq, int irq_flg)
+{
+ if (lrng_pool_highres_timer()) {
+ lrng_time_process();
+ } else {
+ struct pt_regs *regs = get_irq_regs();
+ static atomic_t reg_idx = ATOMIC_INIT(0);
+ u64 ip;
+ u32 tmp;
+
+ if (regs) {
+ u32 *ptr = (u32 *)regs;
+ int reg_ptr = atomic_add_return_relaxed(1, &reg_idx);
+ size_t n = (sizeof(struct pt_regs) / sizeof(u32));
+
+ ip = instruction_pointer(regs);
+ tmp = *(ptr + (reg_ptr % n));
+ tmp = lrng_raw_regs_entropy_store(tmp) ? 0 : tmp;
+ _lrng_pcpu_array_add_u32(tmp);
+ } else {
+ ip = _RET_IP_;
+ }
+
+ lrng_time_process();
+
+ /*
+ * The XOR operation combining the different values is not
+ * considered to destroy entropy since the entirety of all
+ * processed values delivers the entropy (and not each
+ * value separately of the other values).
+ */
+ tmp = lrng_raw_jiffies_entropy_store(jiffies) ? 0 : jiffies;
+ tmp ^= lrng_raw_irq_entropy_store(irq) ? 0 : irq;
+ tmp ^= lrng_raw_irqflags_entropy_store(irq_flg) ? 0 : irq_flg;
+ tmp ^= lrng_raw_retip_entropy_store(ip) ? 0 : ip;
+ tmp ^= ip >> 32;
+ _lrng_pcpu_array_add_u32(tmp);
+ }
+}
+EXPORT_SYMBOL(add_interrupt_randomness);
+
+void lrng_irq_es_state(unsigned char *buf, size_t buflen)
+{
+ const struct lrng_drng *lrng_drng_init = lrng_drng_init_instance();
+
+ /* Assume the lrng_drng_init lock is taken by caller */
+ snprintf(buf, buflen,
+ "IRQ ES properties:\n"
+ " Hash for operating entropy pool: %s\n"
+ " per-CPU interrupt collection size: %u\n"
+ " Standards compliance: %s\n"
+ " High-resolution timer: %s\n"
+ " Continuous compression: %s\n",
+ lrng_drng_init->crypto_cb->lrng_hash_name(),
+ LRNG_DATA_NUM_VALUES,
+ lrng_sp80090b_compliant() ? "SP800-90B " : "",
+ lrng_pool_highres_timer() ? "true" : "false",
+ lrng_pcpu_continuous_compression ? "true" : "false");
+}
diff --git a/drivers/char/lrng/lrng_es_irq.h b/drivers/char/lrng/lrng_es_irq.h
new file mode 100644
index 000000000000..00b16b1aa45f
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_irq.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * LRNG Slow Noise Source: Time stamp array handling
+ *
+ * Copyright (C) 2016 - 2021, Stephan Mueller <[email protected]>
+ */
+
+/*
+ * To limit the impact on the interrupt handling, the LRNG concatenates
+ * entropic LSB parts of the time stamps in a per-CPU array and only
+ * injects them into the entropy pool when the array is full.
+ */
+
+/* Store multiple integers in one u32 */
+#define LRNG_DATA_SLOTSIZE_BITS (8)
+#define LRNG_DATA_SLOTSIZE_MASK ((1 << LRNG_DATA_SLOTSIZE_BITS) - 1)
+#define LRNG_DATA_ARRAY_MEMBER_BITS (4 << 3) /* ((sizeof(u32)) << 3) */
+#define LRNG_DATA_SLOTS_PER_UINT (LRNG_DATA_ARRAY_MEMBER_BITS / \
+ LRNG_DATA_SLOTSIZE_BITS)
+
+/*
+ * Number of time values to store in the array - in small environments
+ * only one atomic_t variable per CPU is used.
+ */
+#define LRNG_DATA_NUM_VALUES (CONFIG_LRNG_COLLECTION_SIZE)
+/* Mask of LSB of time stamp to store */
+#define LRNG_DATA_WORD_MASK (LRNG_DATA_NUM_VALUES - 1)
+
+#define LRNG_DATA_SLOTS_MASK (LRNG_DATA_SLOTS_PER_UINT - 1)
+#define LRNG_DATA_ARRAY_SIZE (LRNG_DATA_NUM_VALUES / \
+ LRNG_DATA_SLOTS_PER_UINT)
+
+/* Starting bit index of slot */
+static inline unsigned int lrng_data_slot2bitindex(unsigned int slot)
+{
+ return (LRNG_DATA_SLOTSIZE_BITS * slot);
+}
+
+/* Convert index into the array index */
+static inline unsigned int lrng_data_idx2array(unsigned int idx)
+{
+ return idx / LRNG_DATA_SLOTS_PER_UINT;
+}
+
+/* Convert index into the slot of a given array index */
+static inline unsigned int lrng_data_idx2slot(unsigned int idx)
+{
+ return idx & LRNG_DATA_SLOTS_MASK;
+}
+
+/* Convert value into slot value */
+static inline unsigned int lrng_data_slot_val(unsigned int val,
+ unsigned int slot)
+{
+ return val << lrng_data_slot2bitindex(slot);
+}
+
+/*
+ * Return the pointers for the previous and current units to inject a u32 into.
+ * Also return the mask which the u32 word is to be processed.
+ */
+static inline void lrng_pcpu_split_u32(u32 *ptr, u32 *pre_ptr, u32 *mask)
+{
+ /* ptr to previous unit */
+ *pre_ptr = (*ptr - LRNG_DATA_SLOTS_PER_UINT) & LRNG_DATA_WORD_MASK;
+ *ptr &= LRNG_DATA_WORD_MASK;
+
+ /* mask to split data into the two parts for the two units */
+ *mask = ((1 << (*pre_ptr & (LRNG_DATA_SLOTS_PER_UINT - 1)) *
+ LRNG_DATA_SLOTSIZE_BITS)) - 1;
+}
--
2.31.1





2021-11-21 16:49:55

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v43 09/15] LRNG - add SP800-90A DRBG extension

Using the LRNG switchable DRNG support, the SP800-90A DRBG extension is
implemented.

The DRBG uses the kernel crypto API DRBG implementation. In addition, it
uses the kernel crypto API SHASH support to provide the hashing
operation.

The DRBG supports the choice of either a CTR DRBG using AES-256, HMAC
DRBG with SHA-512 core or Hash DRBG with SHA-512 core. The used core can
be selected with the module parameter lrng_drbg_type. The default is the
CTR DRBG.

When compiling the DRBG extension statically, the DRBG is loaded at
late_initcall stage which implies that with the start of user space, the
user space interfaces of getrandom(2), /dev/random and /dev/urandom
provide random data produced by an SP800-90A DRBG.

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]>
Reviewed-by: Alexander Lobakin <[email protected]>
Tested-by: Alexander Lobakin <[email protected]>
Reviewed-by: Roman Drahtmueller <[email protected]>
Tested-by: Marcelo Henrique Cerri <[email protected]>
Tested-by: Neil Horman <[email protected]>
Tested-by: Jirka Hladky <[email protected]>
Reviewed-by: Jirka Hladky <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Kconfig | 10 ++
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_drbg.c | 198 ++++++++++++++++++++++++++++++++++
3 files changed, 209 insertions(+)
create mode 100644 drivers/char/lrng/lrng_drbg.c

diff --git a/drivers/char/lrng/Kconfig b/drivers/char/lrng/Kconfig
index 531b34b9ae65..d06d0417b025 100644
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -268,6 +268,16 @@ config LRNG_KCAPI_HASH
bool
select CRYPTO_HASH

+config LRNG_DRBG
+ tristate "SP800-90A support for the LRNG"
+ depends on CRYPTO
+ select CRYPTO_DRBG_MENU
+ select CRYPTO_SHA512
+ select LRNG_KCAPI_HASH
+ help
+ 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.
endif # LRNG_DRNG_SWITCH

endif # LRNG
diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile
index cb9b03bdfd00..c00e15b6396e 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_NUMA) += lrng_numa.o
obj-$(CONFIG_LRNG_CPU) += lrng_es_archrandom.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
diff --git a/drivers/char/lrng/lrng_drbg.c b/drivers/char/lrng/lrng_drbg.c
new file mode 100644
index 000000000000..6ca6b05eccf4
--- /dev/null
+++ b/drivers/char/lrng/lrng_drbg.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Backend for the LRNG providing the cryptographic primitives using the
+ * kernel crypto API and its DRBG.
+ *
+ * Copyright (C) 2016 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <crypto/drbg.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/lrng.h>
+
+#include "lrng_kcapi_hash.h"
+
+/*
+ * Define a DRBG plus a hash / MAC used to extract data from the entropy pool.
+ * For LRNG_HASH_NAME you can use a hash or a MAC (HMAC or CMAC) of your choice
+ * (Note, you should use the suggested selections below -- using SHA-1 or MD5
+ * is not wise). The idea is that the used cipher primitive can be selected to
+ * be the same as used for the DRBG. I.e. the LRNG only uses one cipher
+ * primitive using the same cipher implementation with the options offered in
+ * the following. This means, if the CTR DRBG is selected and AES-NI is present,
+ * both the CTR DRBG and the selected cmac(aes) use AES-NI.
+ *
+ * The security strengths of the DRBGs are all 256 bits according to
+ * SP800-57 section 5.6.1.
+ *
+ * This definition is allowed to be changed.
+ */
+#ifdef CONFIG_CRYPTO_DRBG_CTR
+static unsigned int lrng_drbg_type = 0;
+#elif defined CONFIG_CRYPTO_DRBG_HMAC
+static unsigned int lrng_drbg_type = 1;
+#elif defined CONFIG_CRYPTO_DRBG_HASH
+static unsigned int lrng_drbg_type = 2;
+#else
+#error "Unknown DRBG in use"
+#endif
+
+/* The parameter must be r/o in sysfs as otherwise races appear. */
+module_param(lrng_drbg_type, uint, 0444);
+MODULE_PARM_DESC(lrng_drbg_type, "DRBG type used for LRNG (0->CTR_DRBG, 1->HMAC_DRBG, 2->Hash_DRBG)");
+
+struct lrng_drbg {
+ const char *hash_name;
+ const char *drbg_core;
+};
+
+static const struct lrng_drbg lrng_drbg_types[] = {
+ { /* CTR_DRBG with AES-256 using derivation function */
+ .hash_name = "sha512",
+ .drbg_core = "drbg_nopr_ctr_aes256",
+ }, { /* HMAC_DRBG with SHA-512 */
+ .hash_name = "sha512",
+ .drbg_core = "drbg_nopr_hmac_sha512",
+ }, { /* Hash_DRBG with SHA-512 using derivation function */
+ .hash_name = "sha512",
+ .drbg_core = "drbg_nopr_sha512"
+ }
+};
+
+static int lrng_drbg_drng_seed_helper(void *drng, const u8 *inbuf, u32 inbuflen)
+{
+ struct drbg_state *drbg = (struct drbg_state *)drng;
+ LIST_HEAD(seedlist);
+ struct drbg_string data;
+ int ret;
+
+ drbg_string_fill(&data, inbuf, inbuflen);
+ list_add_tail(&data.list, &seedlist);
+ ret = drbg->d_ops->update(drbg, &seedlist, drbg->seeded);
+
+ if (ret >= 0)
+ drbg->seeded = true;
+
+ return ret;
+}
+
+static int lrng_drbg_drng_generate_helper(void *drng, u8 *outbuf, u32 outbuflen)
+{
+ struct drbg_state *drbg = (struct drbg_state *)drng;
+
+ return drbg->d_ops->generate(drbg, outbuf, outbuflen, NULL);
+}
+
+static void *lrng_drbg_drng_alloc(u32 sec_strength)
+{
+ struct drbg_state *drbg;
+ int coreref = -1;
+ bool pr = false;
+ int ret;
+
+ drbg_convert_tfm_core(lrng_drbg_types[lrng_drbg_type].drbg_core,
+ &coreref, &pr);
+ if (coreref < 0)
+ return ERR_PTR(-EFAULT);
+
+ drbg = kzalloc(sizeof(struct drbg_state), GFP_KERNEL);
+ if (!drbg)
+ return ERR_PTR(-ENOMEM);
+
+ drbg->core = &drbg_cores[coreref];
+ drbg->seeded = false;
+ ret = drbg_alloc_state(drbg);
+ if (ret)
+ goto err;
+
+ if (sec_strength > drbg_sec_strength(drbg->core->flags)) {
+ pr_err("Security strength of DRBG (%u bits) lower than requested by LRNG (%u bits)\n",
+ drbg_sec_strength(drbg->core->flags) * 8,
+ sec_strength * 8);
+ goto dealloc;
+ }
+
+ if (sec_strength < drbg_sec_strength(drbg->core->flags))
+ pr_warn("Security strength of DRBG (%u bits) higher than requested by LRNG (%u bits)\n",
+ drbg_sec_strength(drbg->core->flags) * 8,
+ sec_strength * 8);
+
+ pr_info("DRBG with %s core allocated\n", drbg->core->backend_cra_name);
+
+ return drbg;
+
+dealloc:
+ if (drbg->d_ops)
+ drbg->d_ops->crypto_fini(drbg);
+ drbg_dealloc_state(drbg);
+err:
+ kfree(drbg);
+ return ERR_PTR(-EINVAL);
+}
+
+static void lrng_drbg_drng_dealloc(void *drng)
+{
+ struct drbg_state *drbg = (struct drbg_state *)drng;
+
+ if (drbg && drbg->d_ops)
+ drbg->d_ops->crypto_fini(drbg);
+ drbg_dealloc_state(drbg);
+ kfree_sensitive(drbg);
+ pr_info("DRBG deallocated\n");
+}
+
+static void *lrng_drbg_hash_alloc(void)
+{
+ return lrng_kcapi_hash_alloc(lrng_drbg_types[lrng_drbg_type].hash_name);
+}
+
+static const char *lrng_drbg_name(void)
+{
+ return lrng_drbg_types[lrng_drbg_type].drbg_core;
+}
+
+static const char *lrng_hash_name(void)
+{
+ return lrng_drbg_types[lrng_drbg_type].hash_name;
+}
+
+static const struct lrng_crypto_cb lrng_drbg_crypto_cb = {
+ .lrng_drng_name = lrng_drbg_name,
+ .lrng_hash_name = lrng_hash_name,
+ .lrng_drng_alloc = lrng_drbg_drng_alloc,
+ .lrng_drng_dealloc = lrng_drbg_drng_dealloc,
+ .lrng_drng_seed_helper = lrng_drbg_drng_seed_helper,
+ .lrng_drng_generate_helper = lrng_drbg_drng_generate_helper,
+ .lrng_hash_alloc = lrng_drbg_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_drbg_init(void)
+{
+ if (lrng_drbg_type >= ARRAY_SIZE(lrng_drbg_types)) {
+ pr_err("lrng_drbg_type parameter too large (given %u - max: %lu)",
+ lrng_drbg_type,
+ (unsigned long)ARRAY_SIZE(lrng_drbg_types) - 1);
+ return -EAGAIN;
+ }
+ return lrng_set_drng_cb(&lrng_drbg_crypto_cb);
+}
+
+static void __exit lrng_drbg_exit(void)
+{
+ lrng_set_drng_cb(NULL);
+}
+
+late_initcall(lrng_drbg_init);
+module_exit(lrng_drbg_exit);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Stephan Mueller <[email protected]>");
+MODULE_DESCRIPTION("Linux Random Number Generator - SP800-90A DRBG backend");
--
2.31.1





2021-11-21 16:49:56

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v43 07/15] 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: "Peter, Matthias" <[email protected]>
CC: Marcelo Henrique Cerri <[email protected]>
CC: Neil Horman <[email protected]>
Reviewed-by: Alexander Lobakin <[email protected]>
Tested-by: Alexander Lobakin <[email protected]>
Tested-by: Jirka Hladky <[email protected]>
Reviewed-by: Jirka Hladky <[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 9fea73b72f88..531b34b9ae65 100644
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -262,4 +262,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 2202aa78b63f..cb9b03bdfd00 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_SYSCTL) += lrng_proc.o
obj-$(CONFIG_NUMA) += lrng_numa.o
obj-$(CONFIG_LRNG_CPU) += lrng_es_archrandom.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-11-21 16:50:00

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v43 11/15] crypto: move Jitter RNG header include dir

To support the LRNG operation which uses the Jitter RNG separately
from the kernel crypto API, the header file must be accessible to
the LRNG code.

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]>
Reviewed-by: Alexander Lobakin <[email protected]>
Tested-by: 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]>
Tested-by: Jirka Hladky <[email protected]>
Reviewed-by: Jirka Hladky <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
crypto/jitterentropy-kcapi.c | 3 +--
crypto/jitterentropy.c | 2 +-
{crypto => include/crypto/internal}/jitterentropy.h | 0
3 files changed, 2 insertions(+), 3 deletions(-)
rename {crypto => include/crypto/internal}/jitterentropy.h (100%)

diff --git a/crypto/jitterentropy-kcapi.c b/crypto/jitterentropy-kcapi.c
index e8a4165a1874..c90e60910827 100644
--- a/crypto/jitterentropy-kcapi.c
+++ b/crypto/jitterentropy-kcapi.c
@@ -43,8 +43,7 @@
#include <linux/fips.h>
#include <linux/time.h>
#include <crypto/internal/rng.h>
-
-#include "jitterentropy.h"
+#include <crypto/internal/jitterentropy.h>

/***************************************************************************
* Helper function
diff --git a/crypto/jitterentropy.c b/crypto/jitterentropy.c
index a11b3208760f..f36a391319e1 100644
--- a/crypto/jitterentropy.c
+++ b/crypto/jitterentropy.c
@@ -117,7 +117,7 @@ struct rand_data {
#define JENT_EHEALTH 9 /* Health test failed during initialization */
#define JENT_ERCT 10 /* RCT failed during initialization */

-#include "jitterentropy.h"
+#include <crypto/internal/jitterentropy.h>

/***************************************************************************
* Adaptive Proportion Test
diff --git a/crypto/jitterentropy.h b/include/crypto/internal/jitterentropy.h
similarity index 100%
rename from crypto/jitterentropy.h
rename to include/crypto/internal/jitterentropy.h
--
2.31.1





2021-11-21 16:50:03

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v43 10/15] 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]>
Reviewed-by: Alexander Lobakin <[email protected]>
Tested-by: 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]>
Tested-by: Jirka Hladky <[email protected]>
Reviewed-by: Jirka Hladky <[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 d06d0417b025..25684c54d6a9 100644
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -278,6 +278,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 c00e15b6396e..85abeaa68136 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_LRNG_CPU) += lrng_es_archrandom.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-11-21 16:50:05

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v43 08/15] 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]>
Reviewed-by: Alexander Lobakin <[email protected]>
Tested-by: 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]>
Tested-by: Jirka Hladky <[email protected]>
Reviewed-by: Jirka Hladky <[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 ea85d4a0fe9e..74a9dca67698 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-11-21 16:50:09

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v43 05/15] LRNG - CPU entropy source

Certain CPUs provide instructions giving access to an entropy source
(e.g. RDSEED on Intel/AMD, DARN on POWER, etc.). The LRNG can utilize
the entropy source to seed its DRNG from.

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]>
Reviewed-by: Alexander Lobakin <[email protected]>
Tested-by: Alexander Lobakin <[email protected]>
Mathematical aspects Reviewed-by: "Peter, Matthias" <[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]>
Tested-by: Jirka Hladky <[email protected]>
Reviewed-by: Jirka Hladky <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Kconfig | 38 +++++
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_es_archrandom.c | 226 +++++++++++++++++++++++++
3 files changed, 265 insertions(+)
create mode 100644 drivers/char/lrng/lrng_es_archrandom.c

diff --git a/drivers/char/lrng/Kconfig b/drivers/char/lrng/Kconfig
index a9ae35d9f648..d3abdfa28493 100644
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -215,6 +215,44 @@ config LRNG_IRQ_ENTROPY_RATE
interrupt entropy source will still deliver data but without
being credited with entropy.

+comment "CPU Entropy Source"
+
+config LRNG_CPU
+ bool "Enable CPU Entropy Source as LRNG Seed Source"
+ default y
+ help
+ Current CPUs commonly contain entropy sources which can be
+ used to seed the LRNG. For example, the Intel RDSEED
+ instruction, or the POWER DARN instruction will be sourced
+ to seed the LRNG if this option is enabled.
+
+ Note, if this option is enabled and the underlying CPU
+ does not offer such entropy source, the LRNG will automatically
+ detect this and ignore the hardware.
+
+config LRNG_CPU_FULL_ENT_MULTIPLIER
+ int
+ default 1 if !LRNG_TEST_CPU_ES_COMPRESSION
+ default 123 if LRNG_TEST_CPU_ES_COMPRESSION
+
+config LRNG_CPU_ENTROPY_RATE
+ int "CPU Entropy Source Entropy Rate"
+ depends on LRNG_CPU
+ range 0 256
+ default 8
+ help
+ The option defines the amount of entropy the LRNG applies to 256
+ bits of data obtained from the CPU entropy source. The LRNG
+ enforces the limit that this value must be in the range between
+ 0 and 256.
+
+ When configuring this value to 0, the CPU entropy source will
+ provide 256 bits of data without being credited to contain
+ entropy.
+
+ Note, this option is overwritten when the option
+ CONFIG_RANDOM_TRUST_CPU is set.
+
endmenu # "Entropy Source Configuration"

endif # LRNG
diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile
index 1e722e0967e0..1e18e2c1016b 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -10,3 +10,4 @@ obj-y += lrng_es_mgr.o lrng_aux.o \
obj-$(CONFIG_LRNG_IRQ) += lrng_es_irq.o
obj-$(CONFIG_SYSCTL) += lrng_proc.o
obj-$(CONFIG_NUMA) += lrng_numa.o
+obj-$(CONFIG_LRNG_CPU) += lrng_es_archrandom.o
diff --git a/drivers/char/lrng/lrng_es_archrandom.c b/drivers/char/lrng/lrng_es_archrandom.c
new file mode 100644
index 000000000000..6965f9b8ae50
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_archrandom.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG Fast Entropy Source: CPU-based entropy source
+ *
+ * Copyright (C) 2016 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <crypto/hash.h>
+#include <linux/lrng.h>
+#include <linux/random.h>
+
+#include "lrng_internal.h"
+
+/*
+ * Estimated entropy of data is a 32th of LRNG_DRNG_SECURITY_STRENGTH_BITS.
+ * As we have no ability to review the implementation of those noise sources,
+ * it is prudent to have a conservative estimate here.
+ */
+#define LRNG_ARCHRANDOM_DEFAULT_STRENGTH CONFIG_LRNG_CPU_ENTROPY_RATE
+#define LRNG_ARCHRANDOM_TRUST_CPU_STRENGTH LRNG_DRNG_SECURITY_STRENGTH_BITS
+#ifdef CONFIG_RANDOM_TRUST_CPU
+static u32 archrandom = LRNG_ARCHRANDOM_TRUST_CPU_STRENGTH;
+#else
+static u32 archrandom = LRNG_ARCHRANDOM_DEFAULT_STRENGTH;
+#endif
+#ifdef CONFIG_LRNG_RUNTIME_ES_CONFIG
+module_param(archrandom, uint, 0644);
+MODULE_PARM_DESC(archrandom, "Entropy in bits of 256 data bits from CPU noise source (e.g. RDSEED)");
+#endif
+
+static int __init lrng_parse_trust_cpu(char *arg)
+{
+ int ret;
+ bool trust_cpu = false;
+
+ ret = kstrtobool(arg, &trust_cpu);
+ if (ret)
+ return ret;
+
+ if (trust_cpu) {
+ archrandom = LRNG_ARCHRANDOM_TRUST_CPU_STRENGTH;
+ lrng_pool_add_entropy();
+ } else {
+ archrandom = LRNG_ARCHRANDOM_DEFAULT_STRENGTH;
+ }
+
+ return 0;
+}
+early_param("random.trust_cpu", lrng_parse_trust_cpu);
+
+u32 lrng_archrandom_entropylevel(u32 requested_bits)
+{
+ return lrng_fast_noise_entropylevel(archrandom, requested_bits);
+}
+
+static u32 lrng_get_arch_data(u8 *outbuf, u32 requested_bits)
+{
+ u32 i;
+
+ /* operate on full blocks */
+ BUILD_BUG_ON(LRNG_DRNG_SECURITY_STRENGTH_BYTES % sizeof(unsigned long));
+ BUILD_BUG_ON(CONFIG_LRNG_SEED_BUFFER_INIT_ADD_BITS %
+ sizeof(unsigned long));
+ /* ensure we have aligned buffers */
+ BUILD_BUG_ON(LRNG_KCAPI_ALIGN % sizeof(unsigned long));
+
+ for (i = 0; i < (requested_bits >> 3);
+ i += sizeof(unsigned long)) {
+ if (!arch_get_random_seed_long((unsigned long *)(outbuf + i)) &&
+ !arch_get_random_long((unsigned long *)(outbuf + i))) {
+ archrandom = 0;
+ return 0;
+ }
+ }
+
+ return requested_bits;
+}
+
+static u32 inline lrng_get_arch_data_compress(u8 *outbuf, u32 requested_bits,
+ u32 data_multiplier)
+{
+ SHASH_DESC_ON_STACK(shash, NULL);
+ const struct lrng_crypto_cb *crypto_cb;
+ struct lrng_drng *drng = lrng_drng_init_instance();
+ unsigned long flags;
+ u32 ent_bits = 0, i, partial_bits = 0,
+ full_bits = requested_bits * data_multiplier;
+ void *hash;
+
+ /* Calculate oversampling for SP800-90C */
+ if (lrng_sp80090c_compliant()) {
+ /* Complete amount of bits to be pulled */
+ full_bits += CONFIG_LRNG_OVERSAMPLE_ES_BITS * data_multiplier;
+ /* Full blocks that will be pulled */
+ data_multiplier = full_bits / requested_bits;
+ /* Partial block in bits to be pulled */
+ partial_bits = full_bits - (data_multiplier * requested_bits);
+ }
+
+ lrng_hash_lock(drng, &flags);
+ crypto_cb = drng->crypto_cb;
+ hash = drng->hash;
+
+ if (crypto_cb->lrng_hash_init(shash, hash))
+ goto out;
+
+ /* Hash all data from the CPU entropy source */
+ for (i = 0; i < data_multiplier; i++) {
+ ent_bits = lrng_get_arch_data(outbuf, requested_bits);
+ if (!ent_bits)
+ goto out;
+
+ if (crypto_cb->lrng_hash_update(shash, outbuf, ent_bits >> 3))
+ goto err;
+ }
+
+ /* Hash partial block, if applicable */
+ ent_bits = lrng_get_arch_data(outbuf, partial_bits);
+ if (ent_bits &&
+ crypto_cb->lrng_hash_update(shash, outbuf, ent_bits >> 3))
+ goto err;
+
+ pr_debug("pulled %u bits from CPU RNG entropy source\n", full_bits);
+
+ /* Generate the compressed data to be returned to the caller */
+ ent_bits = crypto_cb->lrng_hash_digestsize(hash) << 3;
+ if (requested_bits < ent_bits) {
+ u8 digest[LRNG_MAX_DIGESTSIZE];
+
+ if (crypto_cb->lrng_hash_final(shash, digest))
+ goto err;
+
+ /* Truncate output data to requested size */
+ memcpy(outbuf, digest, requested_bits >> 3);
+ memzero_explicit(digest, crypto_cb->lrng_hash_digestsize(hash));
+ ent_bits = requested_bits;
+ } else {
+ if (crypto_cb->lrng_hash_final(shash, outbuf))
+ goto err;
+ }
+
+out:
+ crypto_cb->lrng_hash_desc_zero(shash);
+ lrng_hash_unlock(drng, flags);
+ return ent_bits;
+
+err:
+ ent_bits = 0;
+ goto out;
+}
+
+/*
+ * If CPU entropy source requires does not return full entropy, return the
+ * multiplier of how much data shall be sampled from it.
+ */
+static u32 lrng_arch_multiplier(void)
+{
+ static u32 data_multiplier = 0;
+
+ if (data_multiplier > 0) {
+ return data_multiplier;
+ } else {
+ unsigned long v;
+
+ if (IS_ENABLED(CONFIG_X86) && !arch_get_random_seed_long(&v)) {
+ /*
+ * Intel SPEC: pulling 512 blocks from RDRAND ensures
+ * one reseed making it logically equivalent to RDSEED.
+ */
+ data_multiplier = 512;
+ } else if (IS_ENABLED(CONFIG_PPC)) {
+ /*
+ * PowerISA defines DARN to deliver at least 0.5 bits of
+ * entropy per data bit.
+ */
+ data_multiplier = 2;
+ } else {
+ /* CPU provides full entropy */
+ data_multiplier = CONFIG_LRNG_CPU_FULL_ENT_MULTIPLIER;
+ }
+ }
+ return data_multiplier;
+}
+
+/*
+ * lrng_get_arch() - Get CPU entropy source entropy
+ *
+ * @outbuf: buffer to store entropy of size requested_bits
+ *
+ * Return:
+ * * > 0 on success where value provides the added entropy in bits
+ * * 0 if no fast source was available
+ */
+u32 lrng_get_arch(u8 *outbuf, u32 requested_bits)
+{
+ u32 ent_bits, data_multiplier = lrng_arch_multiplier();
+
+ if (data_multiplier <= 1) {
+ ent_bits = lrng_get_arch_data(outbuf, requested_bits);
+ } else {
+ ent_bits = lrng_get_arch_data_compress(outbuf, requested_bits,
+ data_multiplier);
+ }
+
+ ent_bits = lrng_archrandom_entropylevel(ent_bits);
+ pr_debug("obtained %u bits of entropy from CPU RNG entropy source\n",
+ ent_bits);
+ return ent_bits;
+}
+
+void lrng_arch_es_state(unsigned char *buf, size_t buflen)
+{
+ const struct lrng_drng *lrng_drng_init = lrng_drng_init_instance();
+ u32 data_multiplier = lrng_arch_multiplier();
+
+ /* Assume the lrng_drng_init lock is taken by caller */
+ snprintf(buf, buflen,
+ "CPU ES properties:\n"
+ " Hash for compressing data: %s\n"
+ " Data multiplier: %u\n",
+ (data_multiplier <= 1) ?
+ "N/A" : lrng_drng_init->crypto_cb->lrng_hash_name(),
+ data_multiplier);
+}
--
2.31.1





2021-11-21 16:50:22

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v43 14/15] 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]>
Reviewed-by: Alexander Lobakin <[email protected]>
Tested-by: Alexander Lobakin <[email protected]>
Reviewed-by: Roman Drahtmueller <[email protected]>
Tested-by: Marcelo Henrique Cerri <[email protected]>
Tested-by: Neil Horman <[email protected]>
Tested-by: Jirka Hladky <[email protected]>
Reviewed-by: Jirka Hladky <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Kconfig | 180 ++++++++
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_testing.c | 689 +++++++++++++++++++++++++++++++
3 files changed, 870 insertions(+)
create mode 100644 drivers/char/lrng/lrng_testing.c

diff --git a/drivers/char/lrng/Kconfig b/drivers/char/lrng/Kconfig
index bc1b85929b94..72b572cb7df5 100644
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -380,4 +380,184 @@ 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_RUNTIME_MAX_WO_RESEED_CONFIG
+ bool "Enable runtime configuration of max reseed threshold"
+ help
+ When enabling this option, the LRNG provides an interface
+ allowing the setting of the maximum number of DRNG generate
+ operations without a reseed that has full entropy. The
+ interface is lrng_drng.max_wo_reseed.
+
+config LRNG_TEST_CPU_ES_COMPRESSION
+ bool "Force CPU ES compression operation"
+ help
+ When enabling this option, the CPU ES compression operation
+ is forced by setting an arbitrary value > 1 for the data
+ multiplier even when the CPU ES would deliver full entropy.
+ This allows testing of the compression operation. It
+ therefore forces to pull more data from the CPU ES
+ than what may be required.
+
+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 f868f9fe53a6..6d5035aa611e 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_LRNG_DRBG) += lrng_drbg.o
obj-$(CONFIG_LRNG_KCAPI) += lrng_kcapi.o
obj-$(CONFIG_LRNG_JENT) += lrng_es_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-11-21 16:50:25

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v43 13/15] 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]>
Reviewed-by: Alexander Lobakin <[email protected]>
Tested-by: Alexander Lobakin <[email protected]>
Reviewed-by: Roman Drahtmueller <[email protected]>
Tested-by: Marcelo Henrique Cerri <[email protected]>
Tested-by: Neil Horman <[email protected]>
Tested-by: Jirka Hladky <[email protected]>
Reviewed-by: Jirka Hladky <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Kconfig | 58 +++++
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_health.c | 410 ++++++++++++++++++++++++++++++++
3 files changed, 469 insertions(+)
create mode 100644 drivers/char/lrng/lrng_health.c

diff --git a/drivers/char/lrng/Kconfig b/drivers/char/lrng/Kconfig
index 478b12839907..bc1b85929b94 100644
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -196,6 +196,64 @@ config LRNG_COLLECTION_SIZE
default 4096 if LRNG_COLLECTION_SIZE_4096
default 8192 if LRNG_COLLECTION_SIZE_8192

+config LRNG_HEALTH_TESTS
+ bool "Enable interrupt entropy source online health tests"
+ depends on LRNG_IRQ
+ help
+ 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.
+
+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"
depends on LRNG_IRQ
diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile
index c797fbd10a98..f868f9fe53a6 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -16,3 +16,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_es_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-11-21 16:50:29

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v43 12/15] 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]>
Reviewed-by: Alexander Lobakin <[email protected]>
Tested-by: Alexander Lobakin <[email protected]>
Reviewed-by: Marcelo Henrique Cerri <[email protected]>
Tested-by: Marcelo Henrique Cerri <[email protected]>
Tested-by: Neil Horman <[email protected]>
Tested-by: Jirka Hladky <[email protected]>
Reviewed-by: Jirka Hladky <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Kconfig | 29 ++++++++++
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_es_jent.c | 97 ++++++++++++++++++++++++++++++++
3 files changed, 127 insertions(+)
create mode 100644 drivers/char/lrng/lrng_es_jent.c

diff --git a/drivers/char/lrng/Kconfig b/drivers/char/lrng/Kconfig
index 25684c54d6a9..478b12839907 100644
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -215,6 +215,35 @@ 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"
+ depends on LRNG_JENT
+ 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.
+
+ When configuring this value to 0, the Jitter RNG entropy source
+ will provide 256 bits of data without being credited to contain
+ entropy.
+
comment "CPU Entropy Source"

config LRNG_CPU
diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile
index 85abeaa68136..c797fbd10a98 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -15,3 +15,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_es_jent.o
diff --git a/drivers/char/lrng/lrng_es_jent.c b/drivers/char/lrng/lrng_es_jent.c
new file mode 100644
index 000000000000..2ed221cc9372
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_jent.c
@@ -0,0 +1,97 @@
+// 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. */
+ if (jent_entropy_init() ||
+ (lrng_jent_state = jent_entropy_collector_alloc(1, 0)) == NULL) {
+ 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 (!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);
+}
+
+void lrng_jent_es_state(unsigned char *buf, size_t buflen)
+{
+ snprintf(buf, buflen,
+ "JitterRNG ES properties:\n"
+ " Enabled: %s\n", lrng_jent_initialized ? "true" : "false");
+}
--
2.31.1





2021-11-21 16:50:32

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v43 15/15] 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]>
Reviewed-by: Alexander Lobakin <[email protected]>
Tested-by: Alexander Lobakin <[email protected]>
Tested-by: Jirka Hladky <[email protected]>
Reviewed-by: Jirka Hladky <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Kconfig | 26 ++
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_selftest.c | 386 ++++++++++++++++++++++++++++++
3 files changed, 413 insertions(+)
create mode 100644 drivers/char/lrng/lrng_selftest.c

diff --git a/drivers/char/lrng/Kconfig b/drivers/char/lrng/Kconfig
index 72b572cb7df5..6f969ac6e2fa 100644
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -560,4 +560,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 6d5035aa611e..e4f7f9702eb4 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -18,3 +18,4 @@ obj-$(CONFIG_LRNG_KCAPI) += lrng_kcapi.o
obj-$(CONFIG_LRNG_JENT) += lrng_es_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..4412ebd2a5e6
--- /dev/null
+++ b/drivers/char/lrng/lrng_selftest.c
@@ -0,0 +1,386 @@
+// 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"
+
+#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_SEFLTEST_ERROR_GCD (1 << 3)
+#define LRNG_SELFTEST_NOT_EXECUTED 0xffffffff
+
+#ifdef CONFIG_LRNG_IRQ
+
+#include "lrng_es_irq.h"
+
+static u32 lrng_data_selftest_ptr = 0;
+static u32 lrng_data_selftest[LRNG_DATA_ARRAY_SIZE];
+
+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;
+}
+
+static unsigned int lrng_gcd_selftest(void)
+{
+ u32 history[10];
+ unsigned int i;
+
+#define LRNG_GCD_SELFTEST 3
+ for (i = 0; i < ARRAY_SIZE(history); i++)
+ history[i] = i * LRNG_GCD_SELFTEST;
+
+ if (lrng_gcd_analyze(history, ARRAY_SIZE(history)) == LRNG_GCD_SELFTEST)
+ return LRNG_SELFTEST_PASSED;
+
+ pr_err("LRNG GCD self-test FAILED\n");
+ return LRNG_SEFLTEST_ERROR_GCD;
+}
+
+#else /* CONFIG_LRNG_IRQ */
+
+static unsigned int lrng_data_process_selftest(void)
+{
+ return LRNG_SELFTEST_PASSED;
+}
+
+static unsigned int lrng_gcd_selftest(void)
+{
+ return LRNG_SELFTEST_PASSED;
+}
+
+#endif /* CONFIG_LRNG_IRQ */
+
+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++;
+ }
+}
+
+/* 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 unsigned int lrng_selftest_status = LRNG_SELFTEST_NOT_EXECUTED;
+
+static int lrng_selftest(void)
+{
+ unsigned int ret = lrng_data_process_selftest();
+
+ ret |= lrng_chacha20_drng_selftest();
+ ret |= lrng_hash_selftest();
+ ret |= lrng_gcd_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-11-21 16:50:36

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v43 04/15] 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]>
Reviewed-by: Alexander Lobakin <[email protected]>
Tested-by: 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]>
Tested-by: Jirka Hladky <[email protected]>
Reviewed-by: Jirka Hladky <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_numa.c | 122 ++++++++++++++++++++++++++++++++++
2 files changed, 123 insertions(+)
create mode 100644 drivers/char/lrng/lrng_numa.c

diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile
index d7df72a702e4..1e722e0967e0 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -9,3 +9,4 @@ obj-y += lrng_es_mgr.o lrng_aux.o \

obj-$(CONFIG_LRNG_IRQ) += lrng_es_irq.o
obj-$(CONFIG_SYSCTL) += lrng_proc.o
+obj-$(CONFIG_NUMA) += lrng_numa.o
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-11-21 16:50:39

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v43 03/15] 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]>
Reviewed-by: Alexander Lobakin <[email protected]>
Tested-by: 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]>
Tested-by: Jirka Hladky <[email protected]>
Reviewed-by: Jirka Hladky <[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_proc.c | 199 ++++++++++++++++++++++++++++
3 files changed, 200 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 d321d6d21a44..d7df72a702e4 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -8,3 +8,4 @@ obj-y += lrng_es_mgr.o lrng_aux.o \
lrng_interfaces.o lrng_es_aux.o

obj-$(CONFIG_LRNG_IRQ) += lrng_es_irq.o
+obj-$(CONFIG_SYSCTL) += lrng_proc.o
diff --git a/drivers/char/lrng/lrng_interfaces.c b/drivers/char/lrng/lrng_interfaces.c
index 6316a534bb54..b656aaf0c6cb 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_proc.c b/drivers/char/lrng/lrng_proc.c
new file mode 100644
index 000000000000..b48094c32b95
--- /dev/null
+++ b/drivers/char/lrng/lrng_proc.c
@@ -0,0 +1,199 @@
+// 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"
+
+/*
+ * 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_WRITE_WAKEUP_ENTROPY << 3);
+static char lrng_sysctl_bootid[16];
+static int lrng_drng_reseed_max_min;
+
+void lrng_proc_update_max_write_thresh(u32 new_digestsize)
+{
+ lrng_max_write_thresh = (int)new_digestsize;
+ mb();
+}
+
+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[250], irq[200], aux[100], cpu[90], jent[45];
+
+ lrng_drng_lock(lrng_drng_init, &flags);
+ snprintf(buf, sizeof(buf),
+ "DRNG name: %s\n"
+ "LRNG security strength in bits: %d\n"
+ "number of DRNG instances: %u\n"
+ "Standards compliance: %s\n"
+ "Entropy Sources: %s%s%sAuxiliary\n"
+ "LRNG minimally seeded: %s\n"
+ "LRNG fully seeded: %s\n",
+ lrng_drng_init->crypto_cb->lrng_drng_name(),
+ lrng_security_strength(),
+ numa_drngs,
+ lrng_sp80090c_compliant() ? "SP800-90C " : "",
+ IS_ENABLED(CONFIG_LRNG_IRQ) ? "IRQ " : "",
+ IS_ENABLED(CONFIG_LRNG_JENT) ? "JitterRNG " : "",
+ IS_ENABLED(CONFIG_LRNG_CPU) ? "CPU " : "",
+ lrng_state_min_seeded() ? "true" : "false",
+ lrng_state_fully_seeded() ? "true" : "false");
+
+ lrng_aux_es_state(aux, sizeof(aux));
+
+ irq[0] = '\0';
+ lrng_irq_es_state(irq, sizeof(irq));
+
+ jent[0] = '\0';
+ lrng_jent_es_state(jent, sizeof(jent));
+
+ cpu[0] = '\0';
+ lrng_arch_es_state(cpu, sizeof(cpu));
+
+ lrng_drng_unlock(lrng_drng_init, &flags);
+
+ seq_write(m, buf, strlen(buf));
+ seq_write(m, aux, strlen(aux));
+ seq_write(m, irq, strlen(irq));
+ seq_write(m, jent, strlen(jent));
+ seq_write(m, cpu, strlen(cpu));
+
+ 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-11-21 16:50:47

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v43 01/15] Linux Random Number Generator

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

The LRNG framework provides a flexible random number generator which
allows developers and system integrators to achieve different goals
by ensuring that each solution establishes a secure state.

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

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

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

2. The DRNG is seeded by concatenating the data from the following sources
which deliver data and are credited with entropy if enabled:

(a) the output of the IRQ per-CPU entropy pools,

(b) the auxiliary entropy pool,

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

(d) the CPU-based noise source such as Intel RDSEED.

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

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

(a) During boot time, the amount of received entropy at the different
entropy sources are the trigger points to (re)seed the DRNG.

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

3. The IRQ entropy pool collects noise data from interrupt timing.
Any data received by the LRNG from the interrupt noise sources is
inserted into a per-CPU entropy pool using a hash operation that can
be changed during runtime. Per default, SHA-256 is used.

(a) When an interrupt occurs, the 8 least significant bits of the
high-resolution time stamp divided by the greatest common divisor (GCD)
is mixed into the per-CPU entropy pool. This time stamp is credited with
heuristically implied entropy.

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

5. Any data provided from user space by either writing to /dev/random,
/dev/urandom or the IOCTL of RNDADDENTROPY on both device files
are always injected into the auxiliary pool. Also, device drivers may
provide data that is mixed into an auxiliary pool using the same hash
that is used to process the per-CPU entropy pool. This data is not
credited with entropy by the LRNG.

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

When seed data for the DRNG is to be generated, all per-CPU
entropy pools are hashed. The message digest forms the data used for
seeding the DRNG.

To speed up the interrupt handling code of the LRNG, the time stamp
collected for an interrupt event is divided by the greatest common
divisor to eliminate fixed low bits and then truncated to the 8 least
significant bits. 1024 truncated time stamps are concatenated and then
jointly inserted into the per-CPU entropy pool. During boot time,
until the fully seeded stage is reached, each time stamp with its
32 least significant bits is are concatenated. When 1024/32 = 32 such
events are received, they are injected into the per-CPU entropy pool.

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

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

1. Truncation: The received time stamps divided by the GCD are
truncated to 8 least significant bits (or 32 least significant bits
during boot time)

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

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

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

5. Hashing: A message digest of all per-CPU entropy pools and the
is calculated which forms the new auxiliary pool state.

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

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

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

1. The DRNG is reseeded from the entropy sources if the entropy sources
collectively have at least 32 bits of entropy. The goal of this step is
to ensure that the DRNG receives some initial entropy as early as
possible.

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

3. The DRNG is reseeded from the entropy sources if all entropy sources
collectively can provide at least 256 bits.

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

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

The reseeding of the DRNG always ensures that all entropy sources
collectively can deliver at least 128 entropy bits during runtime once
the DRNG is fully seeded.

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

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

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

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

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

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

- If the DRNG was not successfully reseeded after 2^30 generate requests,
the DRNG reverts back to an unseeded stage implying that the blocking
interfaces of /dev/random and getrandom will block again.

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

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

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

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

The LRNG has the following properties:

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

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

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

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

* instantiate one DRNG per NUMA node

* support for runtime switchable output DRNGs

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

* compile-time selectable collection size

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

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

Note, this patch covers the entropy sources manager, the API
implementation, the built-in ChaCha20 DRNG and the auxiliary entropy
pool.

[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]>
Reviewed-by: Alexander Lobakin <[email protected]>
Tested-by: Alexander Lobakin <[email protected]>
Mathematical aspects Reviewed-by: "Peter, Matthias" <[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]>
Tested-by: Jirka Hladky <[email protected]>
Reviewed-by: Jirka Hladky <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
MAINTAINERS | 7 +
drivers/char/Kconfig | 2 +
drivers/char/Makefile | 9 +-
drivers/char/lrng/Kconfig | 58 +++
drivers/char/lrng/Makefile | 8 +
drivers/char/lrng/lrng_aux.c | 136 ++++++
drivers/char/lrng/lrng_chacha20.c | 321 ++++++++++++++
drivers/char/lrng/lrng_chacha20.h | 25 ++
drivers/char/lrng/lrng_drng.c | 451 +++++++++++++++++++
drivers/char/lrng/lrng_es_aux.c | 294 +++++++++++++
drivers/char/lrng/lrng_es_mgr.c | 373 ++++++++++++++++
drivers/char/lrng/lrng_interfaces.c | 656 ++++++++++++++++++++++++++++
drivers/char/lrng/lrng_internal.h | 485 ++++++++++++++++++++
include/linux/lrng.h | 81 ++++
14 files changed, 2905 insertions(+), 1 deletion(-)
create mode 100644 drivers/char/lrng/Kconfig
create mode 100644 drivers/char/lrng/Makefile
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_drng.c
create mode 100644 drivers/char/lrng/lrng_es_aux.c
create mode 100644 drivers/char/lrng/lrng_es_mgr.c
create mode 100644 drivers/char/lrng/lrng_interfaces.c
create mode 100644 drivers/char/lrng/lrng_internal.h
create mode 100644 include/linux/lrng.h

diff --git a/MAINTAINERS b/MAINTAINERS
index c79388b78818..ea4f88da1601 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10817,6 +10817,13 @@ F: Documentation/litmus-tests/
F: Documentation/memory-barriers.txt
F: tools/memory-model/

+LINUX RANDOM NUMBER GENERATOR (LRNG) DRIVER
+M: Stephan Mueller <[email protected]>
+S: Maintained
+W: https://www.chronox.de/lrng.html
+F: drivers/char/lrng/*
+F: include/linux/lrng.h
+
LIS3LV02D ACCELEROMETER DRIVER
M: Eric Piel <[email protected]>
S: Maintained
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 740811893c57..a52d575ca756 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -451,4 +451,6 @@ config RANDOM_TRUST_BOOTLOADER
pool. Otherwise, say N here so it will be regarded as device input that
only mixes the entropy pool.

+source "drivers/char/lrng/Kconfig"
+
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 264eb398fdd4..7371f7464a49 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -3,7 +3,14 @@
# Makefile for the kernel character device drivers.
#

-obj-y += mem.o random.o
+obj-y += mem.o
+
+ifeq ($(CONFIG_LRNG),y)
+ obj-y += lrng/
+else
+ obj-y += random.o
+endif
+
obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o
obj-y += misc.o
obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o
diff --git a/drivers/char/lrng/Kconfig b/drivers/char/lrng/Kconfig
new file mode 100644
index 000000000000..655d873480b0
--- /dev/null
+++ b/drivers/char/lrng/Kconfig
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Linux Random Number Generator configuration
+#
+
+menuconfig LRNG
+ bool "Linux Random Number Generator"
+ select CRYPTO_LIB_SHA256 if CRYPTO
+ help
+ The Linux Random Number Generator (LRNG) is the replacement
+ of the existing /dev/random provided with drivers/char/random.c.
+ It generates entropy from different noise sources and
+ delivers significant entropy during boot.
+
+if LRNG
+
+menu "Specific DRNG seeding strategies"
+
+config LRNG_OVERSAMPLE_ENTROPY_SOURCES
+ bool "Oversample entropy sources"
+ default n
+ help
+ When enabling this option, the entropy sources are
+ over-sampled with the following approach: First, the
+ the entropy sources are requested to provide 64 bits more
+ entropy than the size of the entropy buffer. For example,
+ if the entropy buffer is 256 bits, 320 bits of entropy
+ is requested to fill that buffer.
+
+ Second, the seed operation of the deterministic RNG
+ requests 128 bits more data from each entropy source than
+ the security strength of the DRNG during initialization.
+ A prerequisite for this operation is that the digest size
+ of the used hash must be at least equally large to generate
+ that buffer. If the prerequisite is not met, this
+ oversampling is not applied.
+
+ This strategy is intended to offset the asymptotic entropy
+ increase to reach full entropy in a buffer.
+
+ The strategy is consistent with the requirements in
+ NIST SP800-90C and is only enforced with fips=1.
+
+ If unsure, say N.
+
+config LRNG_OVERSAMPLE_ES_BITS
+ int
+ default 0 if !LRNG_OVERSAMPLE_ENTROPY_SOURCES
+ default 64 if LRNG_OVERSAMPLE_ENTROPY_SOURCES
+
+config LRNG_SEED_BUFFER_INIT_ADD_BITS
+ int
+ default 0 if !LRNG_OVERSAMPLE_ENTROPY_SOURCES
+ default 128 if LRNG_OVERSAMPLE_ENTROPY_SOURCES
+
+endmenu # "Specific DRNG seeding strategies"
+
+endif # LRNG
diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile
new file mode 100644
index 000000000000..6f4603f897cd
--- /dev/null
+++ b/drivers/char/lrng/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Linux Random Number Generator.
+#
+
+obj-y += lrng_es_mgr.o lrng_aux.o \
+ lrng_drng.o lrng_chacha20.o \
+ lrng_interfaces.o lrng_es_aux.o
diff --git a/drivers/char/lrng/lrng_aux.c b/drivers/char/lrng/lrng_aux.c
new file mode 100644
index 000000000000..e3b994f6e4c1
--- /dev/null
+++ b/drivers/char/lrng/lrng_aux.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG auxiliary interfaces
+ *
+ * Copyright (C) 2019 - 2021 Stephan Mueller <[email protected]>
+ * Copyright (C) 2017 Jason A. Donenfeld <[email protected]>. All
+ * Rights Reserved.
+ * Copyright (C) 2016 Jason Cooper <[email protected]>
+ */
+
+#include <linux/mm.h>
+#include <linux/random.h>
+
+#include "lrng_internal.h"
+
+struct batched_entropy {
+ union {
+ u64 entropy_u64[LRNG_DRNG_BLOCKSIZE / sizeof(u64)];
+ u32 entropy_u32[LRNG_DRNG_BLOCKSIZE / sizeof(u32)];
+ };
+ unsigned int position;
+ spinlock_t batch_lock;
+};
+
+/*
+ * Get a random word for internal kernel use only. The quality of the random
+ * number is as good as /dev/urandom, but there is no backtrack protection,
+ * with the goal of being quite fast and not depleting entropy.
+ */
+static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_u64) = {
+ .batch_lock = __SPIN_LOCK_UNLOCKED(batched_entropy_u64.lock),
+};
+
+u64 get_random_u64(void)
+{
+ u64 ret;
+ unsigned long flags;
+ struct batched_entropy *batch;
+
+ lrng_debug_report_seedlevel("get_random_u64");
+
+ batch = raw_cpu_ptr(&batched_entropy_u64);
+ spin_lock_irqsave(&batch->batch_lock, flags);
+ if (batch->position % ARRAY_SIZE(batch->entropy_u64) == 0) {
+ lrng_drng_get_atomic((u8 *)batch->entropy_u64,
+ LRNG_DRNG_BLOCKSIZE);
+ batch->position = 0;
+ }
+ ret = batch->entropy_u64[batch->position++];
+ spin_unlock_irqrestore(&batch->batch_lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(get_random_u64);
+
+static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_u32) = {
+ .batch_lock = __SPIN_LOCK_UNLOCKED(batched_entropy_u32.lock),
+};
+
+u32 get_random_u32(void)
+{
+ u32 ret;
+ unsigned long flags;
+ struct batched_entropy *batch;
+
+ lrng_debug_report_seedlevel("get_random_u32");
+
+ batch = raw_cpu_ptr(&batched_entropy_u32);
+ spin_lock_irqsave(&batch->batch_lock, flags);
+ if (batch->position % ARRAY_SIZE(batch->entropy_u32) == 0) {
+ lrng_drng_get_atomic((u8 *)batch->entropy_u32,
+ LRNG_DRNG_BLOCKSIZE);
+ batch->position = 0;
+ }
+ ret = batch->entropy_u32[batch->position++];
+ spin_unlock_irqrestore(&batch->batch_lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(get_random_u32);
+
+/*
+ * It's important to invalidate all potential batched entropy that might
+ * be stored before the crng is initialized, which we can do lazily by
+ * simply resetting the counter to zero so that it's re-extracted on the
+ * next usage.
+ */
+void invalidate_batched_entropy(void)
+{
+ int cpu;
+ unsigned long flags;
+
+ for_each_possible_cpu(cpu) {
+ struct batched_entropy *batched_entropy;
+
+ batched_entropy = per_cpu_ptr(&batched_entropy_u32, cpu);
+ spin_lock_irqsave(&batched_entropy->batch_lock, flags);
+ batched_entropy->position = 0;
+ spin_unlock(&batched_entropy->batch_lock);
+
+ batched_entropy = per_cpu_ptr(&batched_entropy_u64, cpu);
+ spin_lock(&batched_entropy->batch_lock);
+ batched_entropy->position = 0;
+ spin_unlock_irqrestore(&batched_entropy->batch_lock, flags);
+ }
+}
+
+/*
+ * randomize_page - Generate a random, page aligned address
+ * @start: The smallest acceptable address the caller will take.
+ * @range: The size of the area, starting at @start, within which the
+ * random address must fall.
+ *
+ * If @start + @range would overflow, @range is capped.
+ *
+ * NOTE: Historical use of randomize_range, which this replaces, presumed that
+ * @start was already page aligned. We now align it regardless.
+ *
+ * Return: A page aligned address within [start, start + range). On error,
+ * @start is returned.
+ */
+unsigned long randomize_page(unsigned long start, unsigned long range)
+{
+ if (!PAGE_ALIGNED(start)) {
+ range -= PAGE_ALIGN(start) - start;
+ start = PAGE_ALIGN(start);
+ }
+
+ if (start > ULONG_MAX - range)
+ range = ULONG_MAX - start;
+
+ range >>= PAGE_SHIFT;
+
+ if (range == 0)
+ return start;
+
+ return start + (get_random_long() % range << PAGE_SHIFT);
+}
diff --git a/drivers/char/lrng/lrng_chacha20.c b/drivers/char/lrng/lrng_chacha20.c
new file mode 100644
index 000000000000..51f693c2971f
--- /dev/null
+++ b/drivers/char/lrng/lrng_chacha20.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Backend for the LRNG providing the cryptographic primitives using
+ * ChaCha20 cipher implementations.
+ *
+ * Copyright (C) 2016 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <crypto/chacha.h>
+#include <linux/lrng.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+
+#include "lrng_chacha20.h"
+#include "lrng_internal.h"
+
+/******************************* ChaCha20 DRNG *******************************/
+
+#define CHACHA_BLOCK_WORDS (CHACHA_BLOCK_SIZE / sizeof(u32))
+
+struct chacha20_state {
+ struct chacha20_block block;
+};
+
+/*
+ * Have a static memory blocks for the ChaCha20 DRNG instance to avoid calling
+ * kmalloc too early in the boot cycle. For subsequent allocation requests,
+ * such as per-NUMA-node DRNG instances, kmalloc will be used.
+ */
+struct chacha20_state chacha20 __latent_entropy;
+
+/*
+ * Update of the ChaCha20 state by either using an unused buffer part or by
+ * generating one ChaCha20 block which is half of the state of the ChaCha20.
+ * The block is XORed into the key part of the state. This shall ensure
+ * backtracking resistance as well as a proper mix of the ChaCha20 state once
+ * the key is injected.
+ */
+static void lrng_chacha20_update(struct chacha20_state *chacha20_state,
+ __le32 *buf, u32 used_words)
+{
+ struct chacha20_block *chacha20 = &chacha20_state->block;
+ u32 i;
+ __le32 tmp[CHACHA_BLOCK_WORDS];
+
+ BUILD_BUG_ON(sizeof(struct chacha20_block) != CHACHA_BLOCK_SIZE);
+ BUILD_BUG_ON(CHACHA_BLOCK_SIZE != 2 * CHACHA_KEY_SIZE);
+
+ if (used_words > CHACHA_KEY_SIZE_WORDS) {
+ chacha20_block(&chacha20->constants[0], (u8 *)tmp);
+ for (i = 0; i < CHACHA_KEY_SIZE_WORDS; i++)
+ chacha20->key.u[i] ^= le32_to_cpu(tmp[i]);
+ memzero_explicit(tmp, sizeof(tmp));
+ } else {
+ for (i = 0; i < CHACHA_KEY_SIZE_WORDS; i++)
+ chacha20->key.u[i] ^= le32_to_cpu(buf[i + used_words]);
+ }
+
+ /* Deterministic increment of nonce as required in RFC 7539 chapter 4 */
+ chacha20->nonce[0]++;
+ if (chacha20->nonce[0] == 0) {
+ chacha20->nonce[1]++;
+ if (chacha20->nonce[1] == 0)
+ chacha20->nonce[2]++;
+ }
+
+ /* Leave counter untouched as it is start value is undefined in RFC */
+}
+
+/*
+ * Seed the ChaCha20 DRNG by injecting the input data into the key part of
+ * the ChaCha20 state. If the input data is longer than the ChaCha20 key size,
+ * perform a ChaCha20 operation after processing of key size input data.
+ * This operation shall spread out the entropy into the ChaCha20 state before
+ * new entropy is injected into the key part.
+ */
+static int lrng_cc20_drng_seed_helper(void *drng, const u8 *inbuf, u32 inbuflen)
+{
+ struct chacha20_state *chacha20_state = (struct chacha20_state *)drng;
+ struct chacha20_block *chacha20 = &chacha20_state->block;
+
+ while (inbuflen) {
+ u32 i, todo = min_t(u32, inbuflen, CHACHA_KEY_SIZE);
+
+ for (i = 0; i < todo; i++)
+ chacha20->key.b[i] ^= inbuf[i];
+
+ /* Break potential dependencies between the inbuf key blocks */
+ lrng_chacha20_update(chacha20_state, NULL,
+ CHACHA_BLOCK_WORDS);
+ inbuf += todo;
+ inbuflen -= todo;
+ }
+
+ return 0;
+}
+
+/*
+ * Chacha20 DRNG generation of random numbers: the stream output of ChaCha20
+ * is the random number. After the completion of the generation of the
+ * stream, the entire ChaCha20 state is updated.
+ *
+ * Note, as the ChaCha20 implements a 32 bit counter, we must ensure
+ * that this function is only invoked for at most 2^32 - 1 ChaCha20 blocks
+ * before a reseed or an update happens. This is ensured by the variable
+ * outbuflen which is a 32 bit integer defining the number of bytes to be
+ * generated by the ChaCha20 DRNG. At the end of this function, an update
+ * operation is invoked which implies that the 32 bit counter will never be
+ * overflown in this implementation.
+ */
+static int lrng_cc20_drng_generate_helper(void *drng, u8 *outbuf, u32 outbuflen)
+{
+ struct chacha20_state *chacha20_state = (struct chacha20_state *)drng;
+ struct chacha20_block *chacha20 = &chacha20_state->block;
+ __le32 aligned_buf[CHACHA_BLOCK_WORDS];
+ u32 ret = outbuflen, used = CHACHA_BLOCK_WORDS;
+ int zeroize_buf = 0;
+
+ while (outbuflen >= CHACHA_BLOCK_SIZE) {
+ chacha20_block(&chacha20->constants[0], outbuf);
+ outbuf += CHACHA_BLOCK_SIZE;
+ outbuflen -= CHACHA_BLOCK_SIZE;
+ }
+
+ if (outbuflen) {
+ chacha20_block(&chacha20->constants[0], (u8 *)aligned_buf);
+ memcpy(outbuf, aligned_buf, outbuflen);
+ used = ((outbuflen + sizeof(aligned_buf[0]) - 1) /
+ sizeof(aligned_buf[0]));
+ zeroize_buf = 1;
+ }
+
+ lrng_chacha20_update(chacha20_state, aligned_buf, used);
+
+ if (zeroize_buf)
+ memzero_explicit(aligned_buf, sizeof(aligned_buf));
+
+ return ret;
+}
+
+void lrng_cc20_init_state(struct chacha20_state *state)
+{
+ lrng_cc20_init_rfc7539(&state->block);
+}
+
+/*
+ * Allocation of the DRNG state
+ */
+static void *lrng_cc20_drng_alloc(u32 sec_strength)
+{
+ struct chacha20_state *state = NULL;
+
+ if (sec_strength > CHACHA_KEY_SIZE) {
+ pr_err("Security strength of ChaCha20 DRNG (%u bits) lower than requested by LRNG (%u bits)\n",
+ CHACHA_KEY_SIZE * 8, sec_strength * 8);
+ return ERR_PTR(-EINVAL);
+ }
+ if (sec_strength < CHACHA_KEY_SIZE)
+ pr_warn("Security strength of ChaCha20 DRNG (%u bits) higher than requested by LRNG (%u bits)\n",
+ CHACHA_KEY_SIZE * 8, sec_strength * 8);
+
+ state = kmalloc(sizeof(struct chacha20_state), GFP_KERNEL);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
+ pr_debug("memory for ChaCha20 core allocated\n");
+
+ lrng_cc20_init_state(state);
+
+ return state;
+}
+
+static void lrng_cc20_drng_dealloc(void *drng)
+{
+ struct chacha20_state *chacha20_state = (struct chacha20_state *)drng;
+
+ if (drng == &chacha20) {
+ memzero_explicit(chacha20_state, sizeof(*chacha20_state));
+ pr_debug("static ChaCha20 core zeroized\n");
+ return;
+ }
+
+ pr_debug("ChaCha20 core zeroized and freed\n");
+ kfree_sensitive(chacha20_state);
+}
+
+/******************************* Hash Operation *******************************/
+
+#ifdef CONFIG_CRYPTO_LIB_SHA256
+
+#include <crypto/sha2.h>
+
+static u32 lrng_cc20_hash_digestsize(void *hash)
+{
+ return SHA256_DIGEST_SIZE;
+}
+
+static int lrng_cc20_hash_init(struct shash_desc *shash, void *hash)
+{
+ /*
+ * We do not need a TFM - we only need sufficient space for
+ * struct sha256_state on the stack.
+ */
+ sha256_init(shash_desc_ctx(shash));
+ return 0;
+}
+
+static int lrng_cc20_hash_update(struct shash_desc *shash,
+ const u8 *inbuf, u32 inbuflen)
+{
+ sha256_update(shash_desc_ctx(shash), inbuf, inbuflen);
+ return 0;
+}
+
+static int lrng_cc20_hash_final(struct shash_desc *shash, u8 *digest)
+{
+ sha256_final(shash_desc_ctx(shash), digest);
+ return 0;
+}
+
+static const char *lrng_cc20_hash_name(void)
+{
+ return "SHA-256";
+}
+
+static void lrng_cc20_hash_desc_zero(struct shash_desc *shash)
+{
+ memzero_explicit(shash_desc_ctx(shash), sizeof(struct sha256_state));
+}
+
+#else /* CONFIG_CRYPTO_LIB_SHA256 */
+
+#include <crypto/sha1.h>
+#include <crypto/sha1_base.h>
+
+/*
+ * If the SHA-256 support is not compiled, we fall back to SHA-1 that is always
+ * compiled and present in the kernel.
+ */
+static u32 lrng_cc20_hash_digestsize(void *hash)
+{
+ return SHA1_DIGEST_SIZE;
+}
+
+static void lrng_sha1_block_fn(struct sha1_state *sctx, const u8 *src,
+ int blocks)
+{
+ u32 temp[SHA1_WORKSPACE_WORDS];
+
+ while (blocks--) {
+ sha1_transform(sctx->state, src, temp);
+ src += SHA1_BLOCK_SIZE;
+ }
+ memzero_explicit(temp, sizeof(temp));
+}
+
+static int lrng_cc20_hash_init(struct shash_desc *shash, void *hash)
+{
+ /*
+ * We do not need a TFM - we only need sufficient space for
+ * struct sha1_state on the stack.
+ */
+ sha1_base_init(shash);
+ return 0;
+}
+
+static int lrng_cc20_hash_update(struct shash_desc *shash,
+ const u8 *inbuf, u32 inbuflen)
+{
+ return sha1_base_do_update(shash, inbuf, inbuflen, lrng_sha1_block_fn);
+}
+
+static int lrng_cc20_hash_final(struct shash_desc *shash, u8 *digest)
+{
+ return sha1_base_do_finalize(shash, lrng_sha1_block_fn) ?:
+ sha1_base_finish(shash, digest);
+}
+
+static const char *lrng_cc20_hash_name(void)
+{
+ return "SHA-1";
+}
+
+static void lrng_cc20_hash_desc_zero(struct shash_desc *shash)
+{
+ memzero_explicit(shash_desc_ctx(shash), sizeof(struct sha1_state));
+}
+
+#endif /* CONFIG_CRYPTO_LIB_SHA256 */
+
+static void *lrng_cc20_hash_alloc(void)
+{
+ pr_info("Hash %s allocated\n", lrng_cc20_hash_name());
+ return NULL;
+}
+
+static void lrng_cc20_hash_dealloc(void *hash)
+{
+}
+
+static const char *lrng_cc20_drng_name(void)
+{
+ return "ChaCha20 DRNG";
+}
+
+const struct lrng_crypto_cb lrng_cc20_crypto_cb = {
+ .lrng_drng_name = lrng_cc20_drng_name,
+ .lrng_hash_name = lrng_cc20_hash_name,
+ .lrng_drng_alloc = lrng_cc20_drng_alloc,
+ .lrng_drng_dealloc = lrng_cc20_drng_dealloc,
+ .lrng_drng_seed_helper = lrng_cc20_drng_seed_helper,
+ .lrng_drng_generate_helper = lrng_cc20_drng_generate_helper,
+ .lrng_hash_alloc = lrng_cc20_hash_alloc,
+ .lrng_hash_dealloc = lrng_cc20_hash_dealloc,
+ .lrng_hash_digestsize = lrng_cc20_hash_digestsize,
+ .lrng_hash_init = lrng_cc20_hash_init,
+ .lrng_hash_update = lrng_cc20_hash_update,
+ .lrng_hash_final = lrng_cc20_hash_final,
+ .lrng_hash_desc_zero = lrng_cc20_hash_desc_zero,
+};
diff --git a/drivers/char/lrng/lrng_chacha20.h b/drivers/char/lrng/lrng_chacha20.h
new file mode 100644
index 000000000000..bd0c0bee38f3
--- /dev/null
+++ b/drivers/char/lrng/lrng_chacha20.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * LRNG ChaCha20 definitions
+ *
+ * Copyright (C) 2016 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#include <crypto/chacha.h>
+
+/* State according to RFC 7539 section 2.3 */
+struct chacha20_block {
+ u32 constants[4];
+ union {
+#define CHACHA_KEY_SIZE_WORDS (CHACHA_KEY_SIZE / sizeof(u32))
+ u32 u[CHACHA_KEY_SIZE_WORDS];
+ u8 b[CHACHA_KEY_SIZE];
+ } key;
+ u32 counter;
+ u32 nonce[3];
+};
+
+static inline void lrng_cc20_init_rfc7539(struct chacha20_block *chacha20)
+{
+ chacha_init_consts(chacha20->constants);
+}
diff --git a/drivers/char/lrng/lrng_drng.c b/drivers/char/lrng/lrng_drng.c
new file mode 100644
index 000000000000..1ab533263239
--- /dev/null
+++ b/drivers/char/lrng/lrng_drng.c
@@ -0,0 +1,451 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG DRNG processing
+ *
+ * Copyright (C) 2016 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/fips.h>
+#include <linux/lrng.h>
+
+#include "lrng_internal.h"
+
+/*
+ * Maximum number of seconds between DRNG reseed intervals of the DRNG. Note,
+ * this is enforced with the next request of random numbers from the
+ * DRNG. Setting this value to zero implies a reseeding attempt before every
+ * generated random number.
+ */
+int lrng_drng_reseed_max_time = 600;
+
+static atomic_t lrng_avail = ATOMIC_INIT(0);
+
+DEFINE_MUTEX(lrng_crypto_cb_update);
+
+/* DRNG for /dev/urandom, getrandom(2), get_random_bytes */
+static struct lrng_drng lrng_drng_init = {
+ .drng = &chacha20,
+ .crypto_cb = &lrng_cc20_crypto_cb,
+ .lock = __MUTEX_INITIALIZER(lrng_drng_init.lock),
+ .spin_lock = __SPIN_LOCK_UNLOCKED(lrng_drng_init.spin_lock),
+ .hash_lock = __RW_LOCK_UNLOCKED(lrng_drng_init.hash_lock)
+};
+
+/*
+ * DRNG for get_random_bytes when called in atomic context. This
+ * DRNG will always use the ChaCha20 DRNG. It will never benefit from a
+ * DRNG switch like the "regular" DRNG. If there was no DRNG switch, the atomic
+ * DRNG is identical to the "regular" DRNG.
+ *
+ * The reason for having this is due to the fact that DRNGs other than
+ * the ChaCha20 DRNG may sleep.
+ */
+static struct lrng_drng lrng_drng_atomic = {
+ .drng = &chacha20,
+ .crypto_cb = &lrng_cc20_crypto_cb,
+ .spin_lock = __SPIN_LOCK_UNLOCKED(lrng_drng_atomic.spin_lock),
+ .hash_lock = __RW_LOCK_UNLOCKED(lrng_drng_atomic.hash_lock)
+};
+
+static u32 max_wo_reseed = LRNG_DRNG_MAX_WITHOUT_RESEED;
+#ifdef CONFIG_LRNG_RUNTIME_MAX_WO_RESEED_CONFIG
+module_param(max_wo_reseed, uint, 0444);
+MODULE_PARM_DESC(max_wo_reseed,
+ "Maximum number of DRNG generate operation without full reseed\n");
+#endif
+
+/********************************** Helper ************************************/
+
+bool lrng_get_available(void)
+{
+ return likely(atomic_read(&lrng_avail));
+}
+
+void lrng_set_available(void)
+{
+ atomic_set(&lrng_avail, 1);
+}
+
+struct lrng_drng *lrng_drng_init_instance(void)
+{
+ return &lrng_drng_init;
+}
+
+struct lrng_drng *lrng_drng_atomic_instance(void)
+{
+ return &lrng_drng_atomic;
+}
+
+void lrng_drng_reset(struct lrng_drng *drng)
+{
+ atomic_set(&drng->requests, LRNG_DRNG_RESEED_THRESH);
+ atomic_set(&drng->requests_since_fully_seeded, 0);
+ drng->last_seeded = jiffies;
+ drng->fully_seeded = false;
+ drng->force_reseed = true;
+ pr_debug("reset DRNG\n");
+}
+
+/* Initialize the default DRNG during boot */
+static void lrng_drng_seed(struct lrng_drng *drng);
+void lrng_drngs_init_cc20(bool force_seed)
+{
+ unsigned long flags = 0;
+
+ if (lrng_get_available())
+ return;
+
+ lrng_drng_lock(&lrng_drng_init, &flags);
+ if (lrng_get_available()) {
+ lrng_drng_unlock(&lrng_drng_init, &flags);
+ if (force_seed)
+ goto seed;
+ return;
+ }
+
+ lrng_drng_reset(&lrng_drng_init);
+ lrng_cc20_init_state(&chacha20);
+ lrng_drng_unlock(&lrng_drng_init, &flags);
+
+ lrng_drng_lock(&lrng_drng_atomic, &flags);
+ lrng_drng_reset(&lrng_drng_atomic);
+ /*
+ * We do not initialize the state of the atomic DRNG as it is identical
+ * to the DRNG at this point.
+ */
+ lrng_drng_unlock(&lrng_drng_atomic, &flags);
+
+ lrng_set_available();
+
+seed:
+ /* Seed the DRNG with any entropy available */
+ if (!lrng_pool_trylock()) {
+ lrng_drng_seed(&lrng_drng_init);
+ pr_info("ChaCha20 core initialized with first seeding\n");
+ lrng_pool_unlock();
+ } else {
+ pr_info("ChaCha20 core initialized without seeding\n");
+ }
+}
+
+bool lrng_sp80090c_compliant(void)
+{
+ if (!IS_ENABLED(CONFIG_LRNG_OVERSAMPLE_ENTROPY_SOURCES))
+ return false;
+
+ /* Entropy source hash must be capable of transporting enough entropy */
+ if (lrng_get_digestsize() <
+ (lrng_security_strength() + CONFIG_LRNG_SEED_BUFFER_INIT_ADD_BITS))
+ return false;
+
+ /* SP800-90C only requested in FIPS mode */
+ return fips_enabled;
+}
+
+/************************* Random Number Generation ***************************/
+
+/* Inject a data buffer into the DRNG */
+static void lrng_drng_inject(struct lrng_drng *drng,
+ const u8 *inbuf, u32 inbuflen, bool fully_seeded)
+{
+ const char *drng_type = unlikely(drng == &lrng_drng_atomic) ?
+ "atomic" : "regular";
+ unsigned long flags = 0;
+
+ BUILD_BUG_ON(LRNG_DRNG_RESEED_THRESH > INT_MAX);
+ pr_debug("seeding %s DRNG with %u bytes\n", drng_type, inbuflen);
+ lrng_drng_lock(drng, &flags);
+ if (drng->crypto_cb->lrng_drng_seed_helper(drng->drng,
+ inbuf, inbuflen) < 0) {
+ pr_warn("seeding of %s DRNG failed\n", drng_type);
+ drng->force_reseed = true;
+ } else {
+ int gc = LRNG_DRNG_RESEED_THRESH - atomic_read(&drng->requests);
+
+ pr_debug("%s DRNG stats since last seeding: %lu secs; generate calls: %d\n",
+ drng_type,
+ (time_after(jiffies, drng->last_seeded) ?
+ (jiffies - drng->last_seeded) : 0) / HZ, gc);
+
+ /* Count the numbers of generate ops since last fully seeded */
+ if (fully_seeded)
+ atomic_set(&drng->requests_since_fully_seeded, 0);
+ else
+ atomic_add(gc, &drng->requests_since_fully_seeded);
+
+ drng->last_seeded = jiffies;
+ atomic_set(&drng->requests, LRNG_DRNG_RESEED_THRESH);
+ drng->force_reseed = false;
+
+ if (!drng->fully_seeded) {
+ drng->fully_seeded = fully_seeded;
+ if (drng->fully_seeded)
+ pr_debug("DRNG fully seeded\n");
+ }
+
+ if (drng->drng == lrng_drng_atomic.drng) {
+ lrng_drng_atomic.last_seeded = jiffies;
+ atomic_set(&lrng_drng_atomic.requests,
+ LRNG_DRNG_RESEED_THRESH);
+ lrng_drng_atomic.force_reseed = false;
+ }
+ }
+ lrng_drng_unlock(drng, &flags);
+}
+
+/*
+ * Perform the seeding of the DRNG with data from noise source
+ */
+static inline void _lrng_drng_seed(struct lrng_drng *drng)
+{
+ struct entropy_buf seedbuf __aligned(LRNG_KCAPI_ALIGN);
+
+ lrng_fill_seed_buffer(&seedbuf,
+ lrng_get_seed_entropy_osr(drng->fully_seeded));
+ lrng_init_ops(&seedbuf);
+ lrng_drng_inject(drng, (u8 *)&seedbuf, sizeof(seedbuf),
+ lrng_fully_seeded(drng->fully_seeded, &seedbuf));
+ memzero_explicit(&seedbuf, sizeof(seedbuf));
+}
+
+static int lrng_drng_get(struct lrng_drng *drng, u8 *outbuf, u32 outbuflen);
+static void lrng_drng_seed(struct lrng_drng *drng)
+{
+ _lrng_drng_seed(drng);
+
+ BUILD_BUG_ON(LRNG_MIN_SEED_ENTROPY_BITS >
+ LRNG_DRNG_SECURITY_STRENGTH_BITS);
+
+ /*
+ * Reseed atomic DRNG from current DRNG,
+ *
+ * We can obtain random numbers from DRNG as the lock type
+ * chosen by lrng_drng_get is usable with the current caller.
+ */
+ if ((drng->drng != lrng_drng_atomic.drng) &&
+ (lrng_drng_atomic.force_reseed ||
+ atomic_read(&lrng_drng_atomic.requests) <= 0 ||
+ time_after(jiffies, lrng_drng_atomic.last_seeded +
+ lrng_drng_reseed_max_time * HZ))) {
+ u8 seedbuf[LRNG_DRNG_SECURITY_STRENGTH_BYTES]
+ __aligned(LRNG_KCAPI_ALIGN);
+ int ret = lrng_drng_get(drng, seedbuf, sizeof(seedbuf));
+
+ if (ret < 0) {
+ pr_warn("Error generating random numbers for atomic DRNG: %d\n",
+ ret);
+ } else {
+ lrng_drng_inject(&lrng_drng_atomic, seedbuf, ret, true);
+ }
+ memzero_explicit(&seedbuf, sizeof(seedbuf));
+ }
+}
+
+static inline void _lrng_drng_seed_work(struct lrng_drng *drng, u32 node)
+{
+ pr_debug("reseed triggered by interrupt noise source for DRNG on NUMA node %d\n",
+ node);
+ lrng_drng_seed(drng);
+ if (drng->fully_seeded) {
+ /* Prevent reseed storm */
+ drng->last_seeded += node * 100 * HZ;
+ /* Prevent draining of pool on idle systems */
+ lrng_drng_reseed_max_time += 100;
+ }
+}
+
+/*
+ * DRNG reseed trigger: Kernel thread handler triggered by the schedule_work()
+ */
+void lrng_drng_seed_work(struct work_struct *dummy)
+{
+ struct lrng_drng **lrng_drng = lrng_drng_instances();
+ u32 node;
+
+ if (lrng_drng) {
+ for_each_online_node(node) {
+ struct lrng_drng *drng = lrng_drng[node];
+
+ if (drng && !drng->fully_seeded) {
+ _lrng_drng_seed_work(drng, node);
+ goto out;
+ }
+ }
+ } else {
+ if (!lrng_drng_init.fully_seeded) {
+ _lrng_drng_seed_work(&lrng_drng_init, 0);
+ goto out;
+ }
+ }
+
+ lrng_pool_all_numa_nodes_seeded(true);
+
+out:
+ /* Allow the seeding operation to be called again */
+ lrng_pool_unlock();
+}
+
+/* Force all DRNGs to reseed before next generation */
+void lrng_drng_force_reseed(void)
+{
+ struct lrng_drng **lrng_drng = lrng_drng_instances();
+ u32 node;
+
+ /*
+ * If the initial DRNG is over the reseed threshold, allow a forced
+ * reseed only for the initial DRNG as this is the fallback for all. It
+ * must be kept seeded before all others to keep the LRNG operational.
+ */
+ if (!lrng_drng ||
+ (atomic_read_u32(&lrng_drng_init.requests_since_fully_seeded) >
+ LRNG_DRNG_RESEED_THRESH)) {
+ lrng_drng_init.force_reseed = lrng_drng_init.fully_seeded;
+ pr_debug("force reseed of initial DRNG\n");
+ return;
+ }
+ for_each_online_node(node) {
+ struct lrng_drng *drng = lrng_drng[node];
+
+ if (!drng)
+ continue;
+
+ drng->force_reseed = drng->fully_seeded;
+ pr_debug("force reseed of DRNG on node %u\n", node);
+ }
+ lrng_drng_atomic.force_reseed = lrng_drng_atomic.fully_seeded;
+}
+
+/*
+ * lrng_drng_get() - Get random data out of the DRNG which is reseeded
+ * frequently.
+ *
+ * @outbuf: buffer for storing random data
+ * @outbuflen: length of outbuf
+ *
+ * Return:
+ * * < 0 in error case (DRNG generation or update failed)
+ * * >=0 returning the returned number of bytes
+ */
+static int lrng_drng_get(struct lrng_drng *drng, u8 *outbuf, u32 outbuflen)
+{
+ unsigned long flags = 0;
+ u32 processed = 0;
+
+ if (!outbuf || !outbuflen)
+ return 0;
+
+ outbuflen = min_t(size_t, outbuflen, INT_MAX);
+
+ lrng_drngs_init_cc20(false);
+
+ /* If DRNG operated without proper reseed for too long, block LRNG */
+ BUILD_BUG_ON(LRNG_DRNG_MAX_WITHOUT_RESEED < LRNG_DRNG_RESEED_THRESH);
+ if (atomic_read_u32(&drng->requests_since_fully_seeded) > max_wo_reseed)
+ lrng_unset_fully_seeded(drng);
+
+ while (outbuflen) {
+ u32 todo = min_t(u32, outbuflen, LRNG_DRNG_MAX_REQSIZE);
+ int ret;
+
+ /* All but the atomic DRNG are seeded during generation */
+ if (atomic_dec_and_test(&drng->requests) ||
+ drng->force_reseed ||
+ time_after(jiffies, drng->last_seeded +
+ lrng_drng_reseed_max_time * HZ)) {
+ if (likely(drng != &lrng_drng_atomic)) {
+ if (lrng_pool_trylock()) {
+ drng->force_reseed = true;
+ } else {
+ lrng_drng_seed(drng);
+ lrng_pool_unlock();
+ }
+ }
+ }
+
+ lrng_drng_lock(drng, &flags);
+ ret = drng->crypto_cb->lrng_drng_generate_helper(
+ drng->drng, outbuf + processed, todo);
+ lrng_drng_unlock(drng, &flags);
+ if (ret <= 0) {
+ pr_warn("getting random data from DRNG failed (%d)\n",
+ ret);
+ return -EFAULT;
+ }
+ processed += ret;
+ outbuflen -= ret;
+ }
+
+ return processed;
+}
+
+int lrng_drng_get_atomic(u8 *outbuf, u32 outbuflen)
+{
+ return lrng_drng_get(&lrng_drng_atomic, outbuf, outbuflen);
+}
+
+int lrng_drng_get_sleep(u8 *outbuf, u32 outbuflen)
+{
+ struct lrng_drng **lrng_drng = lrng_drng_instances();
+ struct lrng_drng *drng = &lrng_drng_init;
+ int node = numa_node_id();
+
+ might_sleep();
+
+ if (lrng_drng && lrng_drng[node] && lrng_drng[node]->fully_seeded)
+ drng = lrng_drng[node];
+
+ return lrng_drng_get(drng, outbuf, outbuflen);
+}
+
+/* Reset LRNG such that all existing entropy is gone */
+static void _lrng_reset(struct work_struct *work)
+{
+ struct lrng_drng **lrng_drng = lrng_drng_instances();
+ unsigned long flags = 0;
+
+ if (!lrng_drng) {
+ lrng_drng_lock(&lrng_drng_init, &flags);
+ lrng_drng_reset(&lrng_drng_init);
+ lrng_drng_unlock(&lrng_drng_init, &flags);
+ } else {
+ u32 node;
+
+ for_each_online_node(node) {
+ struct lrng_drng *drng = lrng_drng[node];
+
+ if (!drng)
+ continue;
+ lrng_drng_lock(drng, &flags);
+ lrng_drng_reset(drng);
+ lrng_drng_unlock(drng, &flags);
+ }
+ }
+ lrng_set_entropy_thresh(LRNG_INIT_ENTROPY_BITS);
+
+ lrng_reset_state();
+}
+
+static DECLARE_WORK(lrng_reset_work, _lrng_reset);
+
+void lrng_reset(void)
+{
+ schedule_work(&lrng_reset_work);
+}
+
+/***************************** Initialize LRNG *******************************/
+
+static int __init lrng_init(void)
+{
+ lrng_drngs_init_cc20(false);
+
+ lrng_drngs_numa_alloc();
+ return 0;
+}
+
+late_initcall(lrng_init);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Stephan Mueller <[email protected]>");
+MODULE_DESCRIPTION("Linux Random Number Generator");
diff --git a/drivers/char/lrng/lrng_es_aux.c b/drivers/char/lrng/lrng_es_aux.c
new file mode 100644
index 000000000000..cd51c7311feb
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_aux.c
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG Slow Entropy Source: Auxiliary entropy pool
+ *
+ * Copyright (C) 2016 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/lrng.h>
+
+#include "lrng_internal.h"
+
+/*
+ * This is the auxiliary pool
+ *
+ * The aux pool array is aligned to 8 bytes to comfort the kernel crypto API
+ * cipher implementations of the hash functions used to read the pool: for some
+ * accelerated implementations, we need an alignment to avoid a realignment
+ * which involves memcpy(). The alignment to 8 bytes should satisfy all crypto
+ * implementations.
+ */
+struct lrng_pool {
+ u8 aux_pool[LRNG_POOL_SIZE]; /* Aux pool: digest state */
+ atomic_t aux_entropy_bits;
+ atomic_t digestsize; /* Digest size of used hash */
+ bool initialized; /* Aux pool initialized? */
+
+ /* Serialize read of entropy pool and update of aux pool */
+ spinlock_t lock;
+};
+
+static struct lrng_pool lrng_pool __aligned(LRNG_KCAPI_ALIGN) = {
+ .aux_entropy_bits = ATOMIC_INIT(0),
+ .digestsize = ATOMIC_INIT(LRNG_ATOMIC_DIGEST_SIZE),
+ .initialized = false,
+ .lock = __SPIN_LOCK_UNLOCKED(lrng_pool.lock)
+};
+
+/********************************** Helper ***********************************/
+
+/* Entropy in bits present in aux pool */
+u32 lrng_avail_aux_entropy(void)
+{
+ /* Cap available entropy with max entropy */
+ u32 avail_bits = min_t(u32, lrng_get_digestsize(),
+ atomic_read_u32(&lrng_pool.aux_entropy_bits));
+
+ /* Consider oversampling rate due to aux pool conditioning */
+ return lrng_reduce_by_osr(avail_bits);
+}
+
+/* Set the digest size of the used hash in bytes */
+static inline void lrng_set_digestsize(u32 digestsize)
+{
+ struct lrng_pool *pool = &lrng_pool;
+ u32 ent_bits = atomic_xchg_relaxed(&pool->aux_entropy_bits, 0),
+ old_digestsize = lrng_get_digestsize();
+
+ atomic_set(&lrng_pool.digestsize, digestsize);
+
+ /*
+ * Update the /proc/.../write_wakeup_threshold which must not be larger
+ * than the digest size of the curent conditioning hash.
+ */
+ digestsize <<= 3;
+ lrng_proc_update_max_write_thresh(digestsize);
+ if (lrng_write_wakeup_bits > digestsize)
+ lrng_write_wakeup_bits = digestsize;
+
+ /*
+ * In case the new digest is larger than the old one, cap the available
+ * entropy to the old message digest used to process the existing data.
+ */
+ ent_bits = min_t(u32, ent_bits, old_digestsize);
+ atomic_add(ent_bits, &pool->aux_entropy_bits);
+}
+
+/* Obtain the digest size provided by the used hash in bits */
+u32 lrng_get_digestsize(void)
+{
+ return atomic_read_u32(&lrng_pool.digestsize) << 3;
+}
+
+/* Set entropy content in user-space controllable aux pool */
+void lrng_pool_set_entropy(u32 entropy_bits)
+{
+ atomic_set(&lrng_pool.aux_entropy_bits, entropy_bits);
+}
+
+/*
+ * Replace old with new hash for auxiliary pool handling
+ *
+ * Assumption: the caller must guarantee that the new_cb is available during the
+ * entire operation (e.g. it must hold the write lock against pointer updating).
+ */
+int lrng_aux_switch_hash(const struct lrng_crypto_cb *new_cb, void *new_hash,
+ const struct lrng_crypto_cb *old_cb)
+{
+ struct lrng_pool *pool = &lrng_pool;
+ struct shash_desc *shash = (struct shash_desc *)pool->aux_pool;
+ u8 digest[LRNG_MAX_DIGESTSIZE];
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_LRNG_DRNG_SWITCH))
+ return -EOPNOTSUPP;
+
+ if (unlikely(!pool->initialized))
+ return 0;
+
+ /* Get the aux pool hash with old digest ... */
+ ret = old_cb->lrng_hash_final(shash, digest) ?:
+ /* ... re-initialize the hash with the new digest ... */
+ new_cb->lrng_hash_init(shash, new_hash) ?:
+ /*
+ * ... feed the old hash into the new state. We may feed
+ * uninitialized memory into the new state, but this is
+ * considered no issue and even good as we have some more
+ * uncertainty here.
+ */
+ new_cb->lrng_hash_update(shash, digest, sizeof(digest));
+ if (!ret) {
+ lrng_set_digestsize(new_cb->lrng_hash_digestsize(new_hash));
+ pr_debug("Re-initialize aux entropy pool with hash %s\n",
+ new_cb->lrng_hash_name());
+ }
+
+ memzero_explicit(digest, sizeof(digest));
+ return ret;
+}
+
+/* Insert data into auxiliary pool by using the hash update function. */
+static int
+lrng_pool_insert_aux_locked(const u8 *inbuf, u32 inbuflen, u32 entropy_bits)
+{
+ struct lrng_pool *pool = &lrng_pool;
+ struct shash_desc *shash = (struct shash_desc *)pool->aux_pool;
+ struct lrng_drng *drng = lrng_drng_init_instance();
+ const struct lrng_crypto_cb *crypto_cb;
+ unsigned long flags;
+ void *hash;
+ int ret;
+
+ entropy_bits = min_t(u32, entropy_bits, inbuflen << 3);
+
+ lrng_hash_lock(drng, &flags);
+
+ crypto_cb = drng->crypto_cb;
+ hash = drng->hash;
+
+ if (unlikely(!pool->initialized)) {
+ ret = crypto_cb->lrng_hash_init(shash, hash);
+ if (ret)
+ goto out;
+ pool->initialized = true;
+ }
+
+ ret = crypto_cb->lrng_hash_update(shash, inbuf, inbuflen);
+ if (ret)
+ goto out;
+
+ /*
+ * Cap the available entropy to the hash output size compliant to
+ * SP800-90B section 3.1.5.1 table 1.
+ */
+ entropy_bits += atomic_read_u32(&pool->aux_entropy_bits);
+ atomic_set(&pool->aux_entropy_bits,
+ min_t(u32, entropy_bits,
+ crypto_cb->lrng_hash_digestsize(hash) << 3));
+
+out:
+ lrng_hash_unlock(drng, flags);
+ return ret;
+}
+
+int lrng_pool_insert_aux(const u8 *inbuf, u32 inbuflen, u32 entropy_bits)
+{
+ struct lrng_pool *pool = &lrng_pool;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ ret = lrng_pool_insert_aux_locked(inbuf, inbuflen, entropy_bits);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ lrng_pool_add_entropy();
+
+ return ret;
+}
+
+/************************* Get data from entropy pool *************************/
+
+/*
+ * Get auxiliary entropy pool and its entropy content for seed buffer.
+ * Caller must hold lrng_pool.pool->lock.
+ * @outbuf: buffer to store data in with size requested_bits
+ * @requested_bits: Requested amount of entropy
+ * @return: amount of entropy in outbuf in bits.
+ */
+static inline u32 lrng_get_aux_pool(u8 *outbuf, u32 requested_bits)
+{
+ struct lrng_pool *pool = &lrng_pool;
+ struct shash_desc *shash = (struct shash_desc *)pool->aux_pool;
+ struct lrng_drng *drng = lrng_drng_init_instance();
+ const struct lrng_crypto_cb *crypto_cb;
+ unsigned long flags;
+ void *hash;
+ u32 collected_ent_bits, returned_ent_bits, unused_bits = 0,
+ digestsize;
+ u8 aux_output[LRNG_MAX_DIGESTSIZE];
+
+ if (unlikely(!pool->initialized))
+ return 0;
+
+ lrng_hash_lock(drng, &flags);
+
+ crypto_cb = drng->crypto_cb;
+ hash = drng->hash;
+ digestsize = crypto_cb->lrng_hash_digestsize(hash);
+
+ /* Ensure that no more than the size of aux_pool can be requested */
+ requested_bits = min_t(u32, requested_bits, (LRNG_MAX_DIGESTSIZE << 3));
+
+ /* Cap entropy with entropy counter from aux pool and the used digest */
+ collected_ent_bits = min_t(u32, digestsize << 3,
+ atomic_xchg_relaxed(&pool->aux_entropy_bits, 0));
+
+ /* We collected too much entropy and put the overflow back */
+ if (collected_ent_bits > (requested_bits + lrng_compress_osr())) {
+ /* Amount of bits we collected too much */
+ unused_bits = collected_ent_bits - requested_bits;
+ /* Put entropy back */
+ atomic_add(unused_bits, &pool->aux_entropy_bits);
+ /* Fix collected entropy */
+ collected_ent_bits = requested_bits;
+ }
+
+ /* Apply oversampling: discount requested oversampling rate */
+ returned_ent_bits = lrng_reduce_by_osr(collected_ent_bits);
+
+ pr_debug("obtained %u bits by collecting %u bits of entropy from aux pool, %u bits of entropy remaining\n",
+ returned_ent_bits, collected_ent_bits, unused_bits);
+
+ /* Get the digest for the aux pool to be returned to the caller ... */
+ if (crypto_cb->lrng_hash_final(shash, aux_output) ||
+ /*
+ * ... and re-initialize the aux state. Do not add the aux pool
+ * digest for backward secrecy as it will be added with the
+ * insertion of the complete seed buffer after it has been filled.
+ */
+ crypto_cb->lrng_hash_init(shash, hash)) {
+ returned_ent_bits = 0;
+ } else {
+ /*
+ * Do not truncate the output size exactly to collected_ent_bits
+ * as the aux pool may contain data that is not credited with
+ * entropy, but we want to use them to stir the DRNG state.
+ */
+ memcpy(outbuf, aux_output, requested_bits >> 3);
+ }
+
+ lrng_hash_unlock(drng, flags);
+ memzero_explicit(aux_output, digestsize);
+ return returned_ent_bits;
+}
+
+void lrng_get_backtrack_aux(struct entropy_buf *entropy_buf, u32 requested_bits)
+{
+ struct lrng_pool *pool = &lrng_pool;
+ unsigned long flags;
+
+ /* Ensure aux pool extraction and backtracking op are atomic */
+ spin_lock_irqsave(&pool->lock, flags);
+
+ entropy_buf->a_bits = lrng_get_aux_pool(entropy_buf->a, requested_bits);
+
+ /* Mix the extracted data back into pool for backtracking resistance */
+ if (lrng_pool_insert_aux_locked((u8 *)entropy_buf,
+ sizeof(struct entropy_buf), 0))
+ pr_warn("Backtracking resistance operation failed\n");
+
+ spin_unlock_irqrestore(&pool->lock, flags);
+}
+
+void lrng_aux_es_state(unsigned char *buf, size_t buflen)
+{
+ const struct lrng_drng *lrng_drng_init = lrng_drng_init_instance();
+
+ /* Assume the lrng_drng_init lock is taken by caller */
+ snprintf(buf, buflen,
+ "Auxiliary ES properties:\n"
+ " Hash for operating entropy pool: %s\n",
+ lrng_drng_init->crypto_cb->lrng_hash_name());
+}
diff --git a/drivers/char/lrng/lrng_es_mgr.c b/drivers/char/lrng/lrng_es_mgr.c
new file mode 100644
index 000000000000..c0025ad2b54a
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_mgr.c
@@ -0,0 +1,373 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG Entropy sources management
+ *
+ * Copyright (C) 2016 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <asm/irq_regs.h>
+#include <linux/percpu.h>
+#include <linux/random.h>
+#include <linux/utsname.h>
+#include <linux/workqueue.h>
+
+#include "lrng_internal.h"
+
+struct lrng_state {
+ bool can_invalidate; /* Can invalidate batched entropy? */
+ bool perform_seedwork; /* Can seed work be performed? */
+ bool lrng_operational; /* Is DRNG operational? */
+ bool lrng_fully_seeded; /* Is DRNG fully seeded? */
+ bool lrng_min_seeded; /* Is DRNG minimally seeded? */
+ bool all_online_numa_node_seeded;/* All NUMA DRNGs seeded? */
+
+ /*
+ * To ensure that external entropy providers cannot dominate the
+ * internal noise sources but yet cannot be dominated by internal
+ * noise sources, the following booleans are intended to allow
+ * external to provide seed once when a DRNG reseed occurs. This
+ * triggering of external noise source is performed even when the
+ * entropy pool has sufficient entropy.
+ */
+ bool lrng_seed_hw; /* Allow HW to provide seed */
+ bool lrng_seed_user; /* Allow user space to provide seed */
+
+ atomic_t boot_entropy_thresh; /* Reseed threshold */
+ atomic_t reseed_in_progress; /* Flag for on executing reseed */
+ struct work_struct lrng_seed_work; /* (re)seed work queue */
+};
+
+static struct lrng_state lrng_state = {
+ false, false, false, false, false, false, true, true,
+ .boot_entropy_thresh = ATOMIC_INIT(LRNG_INIT_ENTROPY_BITS),
+ .reseed_in_progress = ATOMIC_INIT(0),
+};
+
+/********************************** Helper ***********************************/
+
+/* External entropy provider is allowed to provide seed data */
+bool lrng_state_exseed_allow(enum lrng_external_noise_source source)
+{
+ if (source == lrng_noise_source_hw)
+ return lrng_state.lrng_seed_hw;
+ return lrng_state.lrng_seed_user;
+}
+
+/* Enable / disable external entropy provider to furnish seed */
+void lrng_state_exseed_set(enum lrng_external_noise_source source, bool type)
+{
+ if (source == lrng_noise_source_hw)
+ lrng_state.lrng_seed_hw = type;
+ else
+ lrng_state.lrng_seed_user = type;
+}
+
+static inline void lrng_state_exseed_allow_all(void)
+{
+ lrng_state_exseed_set(lrng_noise_source_hw, true);
+ lrng_state_exseed_set(lrng_noise_source_user, true);
+}
+
+/*
+ * Reading of the LRNG pool is only allowed by one caller. The reading is
+ * only performed to (re)seed DRNGs. Thus, if this "lock" is already taken,
+ * the reseeding operation is in progress. The caller is not intended to wait
+ * but continue with its other operation.
+ */
+int lrng_pool_trylock(void)
+{
+ return atomic_cmpxchg(&lrng_state.reseed_in_progress, 0, 1);
+}
+
+void lrng_pool_unlock(void)
+{
+ atomic_set(&lrng_state.reseed_in_progress, 0);
+}
+
+/* Set new entropy threshold for reseeding during boot */
+void lrng_set_entropy_thresh(u32 new_entropy_bits)
+{
+ atomic_set(&lrng_state.boot_entropy_thresh, new_entropy_bits);
+}
+
+/*
+ * Reset LRNG state - the entropy counters are reset, but the data that may
+ * or may not have entropy remains in the pools as this data will not hurt.
+ */
+void lrng_reset_state(void)
+{
+ lrng_pool_set_entropy(0);
+ lrng_pcpu_reset();
+ lrng_state.lrng_operational = false;
+ lrng_state.lrng_fully_seeded = false;
+ lrng_state.lrng_min_seeded = false;
+ lrng_state.all_online_numa_node_seeded = false;
+ pr_debug("reset LRNG\n");
+}
+
+/* Set flag that all DRNGs are fully seeded */
+void lrng_pool_all_numa_nodes_seeded(bool set)
+{
+ lrng_state.all_online_numa_node_seeded = set;
+}
+
+/* Return boolean whether LRNG reached minimally seed level */
+bool lrng_state_min_seeded(void)
+{
+ return lrng_state.lrng_min_seeded;
+}
+
+/* Return boolean whether LRNG reached fully seed level */
+bool lrng_state_fully_seeded(void)
+{
+ return lrng_state.lrng_fully_seeded;
+}
+
+/* Return boolean whether LRNG is considered fully operational */
+bool lrng_state_operational(void)
+{
+ return lrng_state.lrng_operational;
+}
+
+/* Policy to check whether entropy buffer contains full seeded entropy */
+bool lrng_fully_seeded(bool fully_seeded, struct entropy_buf *eb)
+{
+ return ((eb->a_bits + eb->b_bits + eb->c_bits + eb->d_bits) >=
+ lrng_get_seed_entropy_osr(fully_seeded));
+}
+
+/* Mark one DRNG as not fully seeded */
+void lrng_unset_fully_seeded(struct lrng_drng *drng)
+{
+ drng->fully_seeded = false;
+ lrng_pool_all_numa_nodes_seeded(false);
+
+ /*
+ * The init DRNG instance must always be fully seeded as this instance
+ * is the fall-back if any of the per-NUMA node DRNG instances is
+ * insufficiently seeded. Thus, we mark the entire LRNG as
+ * non-operational if the initial DRNG becomes not fully seeded.
+ */
+ if (drng == lrng_drng_init_instance() && lrng_state_operational()) {
+ pr_debug("LRNG set to non-operational\n");
+ lrng_state.lrng_operational = false;
+ lrng_state.lrng_fully_seeded = false;
+
+ /* If sufficient entropy is available, reseed now. */
+ lrng_pool_add_entropy();
+ }
+}
+
+/* Policy to enable LRNG operational mode */
+static inline void lrng_set_operational(u32 external_es)
+{
+ /* LRNG is operational if the initial DRNG is fully seeded ... */
+ if (lrng_state.lrng_fully_seeded &&
+ /* ... and either internal ES SP800-90B startup is complete ... */
+ (lrng_sp80090b_startup_complete() ||
+ /* ... or the external ES provided sufficient entropy. */
+ (lrng_get_seed_entropy_osr(lrng_state_fully_seeded()) <=
+ external_es))) {
+ lrng_state.lrng_operational = true;
+ lrng_process_ready_list();
+ lrng_init_wakeup();
+ pr_info("LRNG fully operational\n");
+ }
+}
+
+/* Available entropy in the entire LRNG considering all entropy sources */
+u32 lrng_avail_entropy(void)
+{
+ u32 ent_thresh = lrng_security_strength();
+
+ /*
+ * Apply oversampling during initialization according to SP800-90C as
+ * we request a larger buffer from the ES.
+ */
+ if (lrng_sp80090c_compliant() &&
+ !lrng_state.all_online_numa_node_seeded)
+ ent_thresh += CONFIG_LRNG_SEED_BUFFER_INIT_ADD_BITS;
+
+ return lrng_pcpu_avail_entropy() + lrng_avail_aux_entropy() +
+ lrng_archrandom_entropylevel(ent_thresh) +
+ lrng_jent_entropylevel(ent_thresh);
+}
+
+/*
+ * lrng_init_ops() - Set seed stages of LRNG
+ *
+ * Set the slow noise source reseed trigger threshold. The initial threshold
+ * is set to the minimum data size that can be read from the pool: a word. Upon
+ * reaching this value, the next seed threshold of 128 bits is set followed
+ * by 256 bits.
+ *
+ * @eb: buffer containing the size of entropy currently injected into DRNG
+ */
+void lrng_init_ops(struct entropy_buf *eb)
+{
+ struct lrng_state *state = &lrng_state;
+ u32 requested_bits, seed_bits, external_es;
+
+ if (state->lrng_operational)
+ return;
+
+ requested_bits = lrng_get_seed_entropy_osr(
+ state->all_online_numa_node_seeded);
+
+ /*
+ * Entropy provided by external entropy sources - if they provide
+ * the requested amount of entropy, unblock the interface.
+ */
+ external_es = eb->a_bits + eb->c_bits + eb->d_bits;
+ seed_bits = external_es + eb->b_bits;
+
+ /* DRNG is seeded with full security strength */
+ if (state->lrng_fully_seeded) {
+ lrng_set_operational(external_es);
+ lrng_set_entropy_thresh(requested_bits);
+ } else if (lrng_fully_seeded(state->all_online_numa_node_seeded, eb)) {
+ if (state->can_invalidate)
+ invalidate_batched_entropy();
+
+ state->lrng_fully_seeded = true;
+ lrng_set_operational(external_es);
+ state->lrng_min_seeded = true;
+ pr_info("LRNG fully seeded with %u bits of entropy\n",
+ seed_bits);
+ lrng_set_entropy_thresh(requested_bits);
+ } else if (!state->lrng_min_seeded) {
+
+ /* DRNG is seeded with at least 128 bits of entropy */
+ if (seed_bits >= LRNG_MIN_SEED_ENTROPY_BITS) {
+ if (state->can_invalidate)
+ invalidate_batched_entropy();
+
+ state->lrng_min_seeded = true;
+ pr_info("LRNG minimally seeded with %u bits of entropy\n",
+ seed_bits);
+ lrng_set_entropy_thresh(requested_bits);
+ lrng_init_wakeup();
+
+ /* DRNG is seeded with at least LRNG_INIT_ENTROPY_BITS bits */
+ } else if (seed_bits >= LRNG_INIT_ENTROPY_BITS) {
+ pr_info("LRNG initial entropy level %u bits of entropy\n",
+ seed_bits);
+ lrng_set_entropy_thresh(LRNG_MIN_SEED_ENTROPY_BITS);
+ }
+ }
+}
+
+int __init rand_initialize(void)
+{
+ struct seed {
+ ktime_t time;
+ unsigned long data[(LRNG_MAX_DIGESTSIZE /
+ sizeof(unsigned long))];
+ struct new_utsname utsname;
+ } seed __aligned(LRNG_KCAPI_ALIGN);
+ unsigned int i;
+
+ BUILD_BUG_ON(LRNG_MAX_DIGESTSIZE % sizeof(unsigned long));
+
+ seed.time = ktime_get_real();
+
+ for (i = 0; i < ARRAY_SIZE(seed.data); i++) {
+ if (!arch_get_random_seed_long_early(&(seed.data[i])) &&
+ !arch_get_random_long_early(&seed.data[i]))
+ seed.data[i] = random_get_entropy();
+ }
+ memcpy(&seed.utsname, utsname(), sizeof(*(utsname())));
+
+ lrng_pool_insert_aux((u8 *)&seed, sizeof(seed), 0);
+ memzero_explicit(&seed, sizeof(seed));
+
+ /* Initialize the seed work queue */
+ INIT_WORK(&lrng_state.lrng_seed_work, lrng_drng_seed_work);
+ lrng_state.perform_seedwork = true;
+
+ lrng_drngs_init_cc20(true);
+ invalidate_batched_entropy();
+
+ lrng_state.can_invalidate = true;
+
+ return 0;
+}
+
+/* Interface requesting a reseed of the DRNG */
+void lrng_pool_add_entropy(void)
+{
+ /*
+ * Once all DRNGs are fully seeded, the interrupt noise
+ * sources will not trigger any reseeding any more.
+ */
+ if (likely(lrng_state.all_online_numa_node_seeded))
+ return;
+
+ /* Only try to reseed if the DRNG is alive. */
+ if (!lrng_get_available())
+ return;
+
+ /* Only trigger the DRNG reseed if we have collected entropy. */
+ if (lrng_avail_entropy() <
+ atomic_read_u32(&lrng_state.boot_entropy_thresh))
+ return;
+
+ /* Ensure that the seeding only occurs once at any given time. */
+ if (lrng_pool_trylock())
+ return;
+
+ /* Seed the DRNG with any available noise. */
+ if (lrng_state.perform_seedwork)
+ schedule_work(&lrng_state.lrng_seed_work);
+ else
+ lrng_drng_seed_work(NULL);
+}
+
+/* Fill the seed buffer with data from the noise sources */
+void lrng_fill_seed_buffer(struct entropy_buf *entropy_buf, u32 requested_bits)
+{
+ struct lrng_state *state = &lrng_state;
+ u32 req_ent = lrng_sp80090c_compliant() ?
+ lrng_security_strength() : LRNG_MIN_SEED_ENTROPY_BITS;
+
+ /* Guarantee that requested bits is a multiple of bytes */
+ BUILD_BUG_ON(LRNG_DRNG_SECURITY_STRENGTH_BITS % 8);
+
+ /* always reseed the DRNG with the current time stamp */
+ entropy_buf->now = random_get_entropy();
+
+ /*
+ * Require at least 128 bits of entropy for any reseed. If the LRNG is
+ * operated SP800-90C compliant we want to comply with SP800-90A section
+ * 9.2 mandating that DRNG is reseeded with the security strength.
+ */
+ if (state->lrng_fully_seeded && (lrng_avail_entropy() < req_ent)) {
+ entropy_buf->a_bits = entropy_buf->b_bits = 0;
+ entropy_buf->c_bits = entropy_buf->d_bits = 0;
+ goto wakeup;
+ }
+
+ /* Concatenate the output of the entropy sources. */
+ entropy_buf->b_bits = lrng_pcpu_pool_hash(entropy_buf->b,
+ requested_bits,
+ state->lrng_fully_seeded);
+ entropy_buf->c_bits = lrng_get_arch(entropy_buf->c, requested_bits);
+ entropy_buf->d_bits = lrng_get_jent(entropy_buf->d, requested_bits);
+ lrng_get_backtrack_aux(entropy_buf, requested_bits);
+
+ /* allow external entropy provider to provide seed */
+ lrng_state_exseed_allow_all();
+
+wakeup:
+ /*
+ * Shall we wake up user space writers? This location covers
+ * ensures that the user space provider does not dominate the internal
+ * noise sources since in case the first call of this function finds
+ * sufficient entropy in the entropy pool, it will not trigger the
+ * wakeup. This implies that when the next /dev/urandom read happens,
+ * the entropy pool is drained.
+ */
+ lrng_writer_wakeup();
+}
diff --git a/drivers/char/lrng/lrng_interfaces.c b/drivers/char/lrng/lrng_interfaces.c
new file mode 100644
index 000000000000..6316a534bb54
--- /dev/null
+++ b/drivers/char/lrng/lrng_interfaces.c
@@ -0,0 +1,656 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG User and kernel space interfaces
+ *
+ * Copyright (C) 2016 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/freezer.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/hw_random.h>
+#include <linux/kthread.h>
+#include <linux/poll.h>
+#include <linux/preempt.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/timex.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/random.h>
+
+#include "lrng_internal.h"
+
+/*
+ * If the entropy count falls under this number of bits, then we
+ * should wake up processes which are selecting or polling on write
+ * access to /dev/random.
+ */
+u32 lrng_write_wakeup_bits = (LRNG_WRITE_WAKEUP_ENTROPY << 3);
+
+static LIST_HEAD(lrng_ready_list);
+static DEFINE_SPINLOCK(lrng_ready_list_lock);
+
+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? */
+static inline bool lrng_need_entropy(void)
+{
+ return (lrng_avail_aux_entropy() < lrng_write_wakeup_bits);
+}
+
+void lrng_writer_wakeup(void)
+{
+ if (lrng_need_entropy() && wq_has_sleeper(&lrng_write_wait)) {
+ wake_up_interruptible(&lrng_write_wait);
+ kill_fasync(&fasync, SIGIO, POLL_OUT);
+ }
+}
+
+void lrng_init_wakeup(void)
+{
+ wake_up_all(&lrng_init_wait);
+ kill_fasync(&fasync, SIGIO, POLL_IN);
+}
+
+/**
+ * lrng_process_ready_list() - Ping all kernel internal callers waiting until
+ * the DRNG is completely initialized to inform that the DRNG reached that
+ * seed level.
+ *
+ * When the SP800-90B testing is enabled, the ping only happens if the SP800-90B
+ * startup health tests are completed. This implies that kernel internal
+ * callers always have an SP800-90B compliant noise source when being
+ * pinged.
+ */
+void lrng_process_ready_list(void)
+{
+ unsigned long flags;
+ struct random_ready_callback *rdy, *tmp;
+
+ if (!lrng_state_operational())
+ return;
+
+ spin_lock_irqsave(&lrng_ready_list_lock, flags);
+ list_for_each_entry_safe(rdy, tmp, &lrng_ready_list, list) {
+ struct module *owner = rdy->owner;
+
+ list_del_init(&rdy->list);
+ rdy->func(rdy);
+ module_put(owner);
+ }
+ spin_unlock_irqrestore(&lrng_ready_list_lock, flags);
+}
+
+void lrng_debug_report_seedlevel(const char *name)
+{
+#ifdef CONFIG_WARN_ALL_UNSEEDED_RANDOM
+ static void *previous = NULL;
+ void *caller = (void *) _RET_IP_;
+
+ if (READ_ONCE(previous) == caller)
+ return;
+
+ if (!lrng_state_min_seeded())
+ pr_notice("%pS %s called without reaching minimally seeded level (available entropy %u)\n",
+ caller, name, lrng_avail_entropy());
+
+ WRITE_ONCE(previous, caller);
+#endif
+}
+
+/************************ LRNG kernel input interfaces ************************/
+
+/*
+ * add_hwgenerator_randomness() - Interface for in-kernel drivers of true
+ * hardware RNGs.
+ *
+ * Those devices may produce endless random bits and will be throttled
+ * when our pool is full.
+ *
+ * @buffer: buffer holding the entropic data from HW noise sources to be used to
+ * insert into entropy pool.
+ * @count: length of buffer
+ * @entropy_bits: amount of entropy in buffer (value is in bits)
+ */
+void add_hwgenerator_randomness(const char *buffer, size_t count,
+ size_t entropy_bits)
+{
+ /*
+ * Suspend writing if we are fully loaded with entropy.
+ * We'll be woken up again once below lrng_write_wakeup_thresh,
+ * or when the calling thread is about to terminate.
+ */
+ wait_event_interruptible(lrng_write_wait,
+ lrng_need_entropy() ||
+ lrng_state_exseed_allow(lrng_noise_source_hw) ||
+ kthread_should_stop());
+ lrng_state_exseed_set(lrng_noise_source_hw, false);
+ lrng_pool_insert_aux(buffer, count, entropy_bits);
+}
+EXPORT_SYMBOL_GPL(add_hwgenerator_randomness);
+
+/*
+ * add_bootloader_randomness() - Handle random seed passed by bootloader.
+ *
+ * If the seed is trustworthy, it would be regarded as hardware RNGs. Otherwise
+ * it would be regarded as device data.
+ * The decision is controlled by CONFIG_RANDOM_TRUST_BOOTLOADER.
+ *
+ * @buf: buffer holding the entropic data from HW noise sources to be used to
+ * insert into entropy pool.
+ * @size: length of buffer
+ */
+void add_bootloader_randomness(const void *buf, unsigned int size)
+{
+ lrng_pool_insert_aux(buf, size,
+ IS_ENABLED(CONFIG_RANDOM_TRUST_BOOTLOADER) ?
+ size * 8 : 0);
+}
+EXPORT_SYMBOL_GPL(add_bootloader_randomness);
+
+/*
+ * Callback for HID layer -- use the HID event values to stir the entropy pool
+ */
+void add_input_randomness(unsigned int type, unsigned int code,
+ unsigned int value)
+{
+ static unsigned char last_value;
+
+ /* ignore autorepeat and the like */
+ if (value == last_value)
+ return;
+
+ last_value = value;
+
+ lrng_pcpu_array_add_u32((type << 4) ^ code ^ (code >> 4) ^ value);
+}
+EXPORT_SYMBOL_GPL(add_input_randomness);
+
+/*
+ * add_device_randomness() - Add device- or boot-specific data to the entropy
+ * pool to help initialize it.
+ *
+ * None of this adds any entropy; it is meant to avoid the problem of
+ * the entropy pool having similar initial state across largely
+ * identical devices.
+ *
+ * @buf: buffer holding the entropic data from HW noise sources to be used to
+ * insert into entropy pool.
+ * @size: length of buffer
+ */
+void add_device_randomness(const void *buf, unsigned int size)
+{
+ lrng_pool_insert_aux((u8 *)buf, size, 0);
+}
+EXPORT_SYMBOL(add_device_randomness);
+
+#ifdef CONFIG_BLOCK
+void rand_initialize_disk(struct gendisk *disk) { }
+void add_disk_randomness(struct gendisk *disk) { }
+EXPORT_SYMBOL(add_disk_randomness);
+#endif
+
+#ifndef CONFIG_LRNG_IRQ
+void add_interrupt_randomness(int irq, int irq_flg) { }
+EXPORT_SYMBOL(add_interrupt_randomness);
+#endif
+
+/*
+ * del_random_ready_callback() - Delete a previously registered readiness
+ * callback function.
+ *
+ * @rdy: callback definition that was registered initially
+ */
+void del_random_ready_callback(struct random_ready_callback *rdy)
+{
+ unsigned long flags;
+ struct module *owner = NULL;
+
+ spin_lock_irqsave(&lrng_ready_list_lock, flags);
+ if (!list_empty(&rdy->list)) {
+ list_del_init(&rdy->list);
+ owner = rdy->owner;
+ }
+ spin_unlock_irqrestore(&lrng_ready_list_lock, flags);
+
+ module_put(owner);
+}
+EXPORT_SYMBOL(del_random_ready_callback);
+
+/*
+ * add_random_ready_callback() - Add a callback function that will be invoked
+ * when the DRNG is fully initialized and seeded.
+ *
+ * @rdy: callback definition to be invoked when the LRNG is seeded
+ *
+ * Return:
+ * * 0 if callback is successfully added
+ * * -EALREADY if pool is already initialised (callback not called)
+ * * -ENOENT if module for callback is not alive
+ */
+int add_random_ready_callback(struct random_ready_callback *rdy)
+{
+ struct module *owner;
+ unsigned long flags;
+ int err = -EALREADY;
+
+ if (likely(lrng_state_operational()))
+ return err;
+
+ owner = rdy->owner;
+ if (!try_module_get(owner))
+ return -ENOENT;
+
+ spin_lock_irqsave(&lrng_ready_list_lock, flags);
+ if (lrng_state_operational())
+ goto out;
+
+ owner = NULL;
+
+ list_add(&rdy->list, &lrng_ready_list);
+ err = 0;
+
+out:
+ spin_unlock_irqrestore(&lrng_ready_list_lock, flags);
+
+ module_put(owner);
+
+ return err;
+}
+EXPORT_SYMBOL(add_random_ready_callback);
+
+/*********************** LRNG kernel output interfaces ************************/
+
+/*
+ * get_random_bytes() - Provider of cryptographic strong random numbers for
+ * kernel-internal usage.
+ *
+ * This function is appropriate for all in-kernel use cases. However,
+ * it will always use the ChaCha20 DRNG.
+ *
+ * @buf: buffer to store the random bytes
+ * @nbytes: size of the buffer
+ */
+void get_random_bytes(void *buf, int nbytes)
+{
+ lrng_drng_get_atomic((u8 *)buf, (u32)nbytes);
+ lrng_debug_report_seedlevel("get_random_bytes");
+}
+EXPORT_SYMBOL(get_random_bytes);
+
+/*
+ * get_random_bytes_full() - Provider of cryptographic strong random numbers
+ * for kernel-internal usage.
+ *
+ * This function is appropriate only for non-atomic use cases as this
+ * function may sleep. Though, it provides access to the full functionality
+ * of LRNG including the switchable DRNG support, that may support other
+ * DRNGs such as the SP800-90A DRBG.
+ *
+ * @buf: buffer to store the random bytes
+ * @nbytes: size of the buffer
+ */
+void get_random_bytes_full(void *buf, int nbytes)
+{
+ lrng_drng_get_sleep((u8 *)buf, (u32)nbytes);
+ lrng_debug_report_seedlevel("get_random_bytes_full");
+}
+EXPORT_SYMBOL(get_random_bytes_full);
+
+/*
+ * wait_for_random_bytes() - Wait for the LRNG to be seeded and thus
+ * guaranteed to supply cryptographically secure random numbers.
+ *
+ * This applies to: the /dev/urandom device, the get_random_bytes function,
+ * and the get_random_{u32,u64,int,long} family of functions. Using any of
+ * these functions without first calling this function forfeits the guarantee
+ * of security.
+ *
+ * Return:
+ * * 0 if the LRNG has been seeded.
+ * * -ERESTARTSYS if the function was interrupted by a signal.
+ */
+int wait_for_random_bytes(void)
+{
+ if (likely(lrng_state_min_seeded()))
+ return 0;
+ return wait_event_interruptible(lrng_init_wait,
+ lrng_state_min_seeded());
+}
+EXPORT_SYMBOL(wait_for_random_bytes);
+
+/*
+ * get_random_bytes_arch() - This function will use the architecture-specific
+ * hardware random number generator if it is available.
+ *
+ * The arch-specific hw RNG will almost certainly be faster than what we can
+ * do in software, but it is impossible to verify that it is implemented
+ * securely (as opposed, to, say, the AES encryption of a sequence number using
+ * a key known by the NSA). So it's useful if we need the speed, but only if
+ * we're willing to trust the hardware manufacturer not to have put in a back
+ * door.
+ *
+ * @buf: buffer allocated by caller to store the random data in
+ * @nbytes: length of outbuf
+ *
+ * Return: number of bytes filled in.
+ */
+int __must_check get_random_bytes_arch(void *buf, int nbytes)
+{
+ u8 *p = buf;
+
+ while (nbytes) {
+ unsigned long v;
+ int chunk = min_t(int, nbytes, sizeof(unsigned long));
+
+ if (!arch_get_random_long(&v))
+ break;
+
+ memcpy(p, &v, chunk);
+ p += chunk;
+ nbytes -= chunk;
+ }
+
+ if (nbytes)
+ lrng_drng_get_atomic((u8 *)p, (u32)nbytes);
+
+ return nbytes;
+}
+EXPORT_SYMBOL(get_random_bytes_arch);
+
+/*
+ * Returns whether or not the LRNG has been seeded.
+ *
+ * Returns: true if the urandom pool has been seeded.
+ * false if the urandom pool has not been seeded.
+ */
+bool rng_is_initialized(void)
+{
+ return lrng_state_operational();
+}
+EXPORT_SYMBOL(rng_is_initialized);
+
+/************************ LRNG user output interfaces *************************/
+
+static ssize_t lrng_read_common(char __user *buf, size_t nbytes)
+{
+ ssize_t ret = 0;
+ u8 tmpbuf[LRNG_DRNG_BLOCKSIZE] __aligned(LRNG_KCAPI_ALIGN);
+ u8 *tmp_large = NULL, *tmp = tmpbuf;
+ u32 tmplen = sizeof(tmpbuf);
+
+ if (nbytes == 0)
+ return 0;
+
+ /*
+ * Satisfy large read requests -- as the common case are smaller
+ * request sizes, such as 16 or 32 bytes, avoid a kmalloc overhead for
+ * those by using the stack variable of tmpbuf.
+ */
+ if (!CONFIG_BASE_SMALL && (nbytes > sizeof(tmpbuf))) {
+ tmplen = min_t(u32, nbytes, LRNG_DRNG_MAX_REQSIZE);
+ tmp_large = kmalloc(tmplen + LRNG_KCAPI_ALIGN, GFP_KERNEL);
+ if (!tmp_large)
+ tmplen = sizeof(tmpbuf);
+ else
+ tmp = PTR_ALIGN(tmp_large, LRNG_KCAPI_ALIGN);
+ }
+
+ while (nbytes) {
+ u32 todo = min_t(u32, nbytes, tmplen);
+ int rc = 0;
+
+ /* Reschedule if we received a large request. */
+ if ((tmp_large) && need_resched()) {
+ if (signal_pending(current)) {
+ if (ret == 0)
+ ret = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+
+ rc = lrng_drng_get_sleep(tmp, todo);
+ if (rc <= 0) {
+ if (rc < 0)
+ ret = rc;
+ break;
+ }
+ if (copy_to_user(buf, tmp, rc)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ nbytes -= rc;
+ buf += rc;
+ ret += rc;
+ }
+
+ /* Wipe data just returned from memory */
+ if (tmp_large)
+ kfree_sensitive(tmp_large);
+ else
+ memzero_explicit(tmpbuf, sizeof(tmpbuf));
+
+ return ret;
+}
+
+static ssize_t
+lrng_read_common_block(int nonblock, char __user *buf, size_t nbytes)
+{
+ if (nbytes == 0)
+ return 0;
+
+ if (unlikely(!lrng_state_operational())) {
+ int ret;
+
+ if (nonblock)
+ return -EAGAIN;
+
+ ret = wait_event_interruptible(lrng_init_wait,
+ lrng_state_operational());
+ if (unlikely(ret))
+ return ret;
+ }
+
+ return lrng_read_common(buf, nbytes);
+}
+
+static ssize_t lrng_drng_read_block(struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ return lrng_read_common_block(file->f_flags & O_NONBLOCK, buf, nbytes);
+}
+
+static __poll_t lrng_random_poll(struct file *file, poll_table *wait)
+{
+ __poll_t mask;
+
+ poll_wait(file, &lrng_init_wait, wait);
+ poll_wait(file, &lrng_write_wait, wait);
+ mask = 0;
+ if (lrng_state_operational())
+ mask |= EPOLLIN | EPOLLRDNORM;
+ if (lrng_need_entropy() ||
+ lrng_state_exseed_allow(lrng_noise_source_user)) {
+ lrng_state_exseed_set(lrng_noise_source_user, false);
+ mask |= EPOLLOUT | EPOLLWRNORM;
+ }
+ return mask;
+}
+
+static ssize_t lrng_drng_write_common(const char __user *buffer, size_t count,
+ u32 entropy_bits)
+{
+ ssize_t ret = 0;
+ u8 buf[64] __aligned(LRNG_KCAPI_ALIGN);
+ const char __user *p = buffer;
+ u32 orig_entropy_bits = entropy_bits;
+
+ if (!lrng_get_available())
+ return -EAGAIN;
+
+ count = min_t(size_t, count, INT_MAX);
+ while (count > 0) {
+ size_t bytes = min_t(size_t, count, sizeof(buf));
+ u32 ent = min_t(u32, bytes<<3, entropy_bits);
+
+ if (copy_from_user(&buf, p, bytes))
+ return -EFAULT;
+ /* Inject data into entropy pool */
+ lrng_pool_insert_aux(buf, bytes, ent);
+
+ count -= bytes;
+ p += bytes;
+ ret += bytes;
+ entropy_bits -= ent;
+
+ cond_resched();
+ }
+
+ /* Force reseed of DRNG during next data request. */
+ if (!orig_entropy_bits)
+ lrng_drng_force_reseed();
+
+ return ret;
+}
+
+static ssize_t lrng_drng_read(struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ if (!lrng_state_min_seeded())
+ pr_notice_ratelimited("%s - use of insufficiently seeded DRNG (%zu bytes read)\n",
+ current->comm, nbytes);
+ else if (!lrng_state_operational())
+ pr_debug_ratelimited("%s - use of not fully seeded DRNG (%zu bytes read)\n",
+ current->comm, nbytes);
+
+ return lrng_read_common(buf, nbytes);
+}
+
+static ssize_t lrng_drng_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ return lrng_drng_write_common(buffer, count, 0);
+}
+
+static long lrng_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+ u32 digestsize_bits;
+ int size, ent_count_bits;
+ int __user *p = (int __user *)arg;
+
+ switch (cmd) {
+ case RNDGETENTCNT:
+ ent_count_bits = lrng_avail_entropy();
+ if (put_user(ent_count_bits, p))
+ return -EFAULT;
+ return 0;
+ case RNDADDTOENTCNT:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (get_user(ent_count_bits, p))
+ return -EFAULT;
+ ent_count_bits = (int)lrng_avail_aux_entropy() + ent_count_bits;
+ if (ent_count_bits < 0)
+ ent_count_bits = 0;
+ digestsize_bits = lrng_get_digestsize();
+ if (ent_count_bits > digestsize_bits)
+ ent_count_bits = digestsize_bits;
+ lrng_pool_set_entropy(ent_count_bits);
+ return 0;
+ case RNDADDENTROPY:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (get_user(ent_count_bits, p++))
+ return -EFAULT;
+ if (ent_count_bits < 0)
+ return -EINVAL;
+ if (get_user(size, p++))
+ return -EFAULT;
+ if (size < 0)
+ return -EINVAL;
+ /* there cannot be more entropy than data */
+ ent_count_bits = min(ent_count_bits, size<<3);
+ return lrng_drng_write_common((const char __user *)p, size,
+ ent_count_bits);
+ case RNDZAPENTCNT:
+ case RNDCLEARPOOL:
+ /* Clear the entropy pool counter. */
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ lrng_pool_set_entropy(0);
+ return 0;
+ case RNDRESEEDCRNG:
+ /*
+ * We leave the capability check here since it is present
+ * in the upstream's RNG implementation. Yet, user space
+ * can trigger a reseed as easy as writing into /dev/random
+ * or /dev/urandom where no privilege is needed.
+ */
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ /* Force a reseed of all DRNGs */
+ lrng_drng_force_reseed();
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int lrng_fasync(int fd, struct file *filp, int on)
+{
+ return fasync_helper(fd, filp, on, &fasync);
+}
+
+const struct file_operations random_fops = {
+ .read = lrng_drng_read_block,
+ .write = lrng_drng_write,
+ .poll = lrng_random_poll,
+ .unlocked_ioctl = lrng_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+ .fasync = lrng_fasync,
+ .llseek = noop_llseek,
+};
+
+const struct file_operations urandom_fops = {
+ .read = lrng_drng_read,
+ .write = lrng_drng_write,
+ .unlocked_ioctl = lrng_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+ .fasync = lrng_fasync,
+ .llseek = noop_llseek,
+};
+
+SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count,
+ unsigned int, flags)
+{
+ if (flags & ~(GRND_NONBLOCK|GRND_RANDOM|GRND_INSECURE))
+ return -EINVAL;
+
+ /*
+ * Requesting insecure and blocking randomness at the same time makes
+ * no sense.
+ */
+ if ((flags &
+ (GRND_INSECURE|GRND_RANDOM)) == (GRND_INSECURE|GRND_RANDOM))
+ return -EINVAL;
+
+ if (count > INT_MAX)
+ count = INT_MAX;
+
+ if (flags & GRND_INSECURE)
+ return lrng_drng_read(NULL, buf, count, NULL);
+
+ return lrng_read_common_block(flags & GRND_NONBLOCK, buf, count);
+}
diff --git a/drivers/char/lrng/lrng_internal.h b/drivers/char/lrng/lrng_internal.h
new file mode 100644
index 000000000000..d67aa3c335b9
--- /dev/null
+++ b/drivers/char/lrng/lrng_internal.h
@@ -0,0 +1,485 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2018 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#ifndef _LRNG_INTERNAL_H
+#define _LRNG_INTERNAL_H
+
+#include <crypto/sha1.h>
+#include <crypto/sha2.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rwlock.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+/*************************** General LRNG parameter ***************************/
+
+/* Security strength of LRNG -- this must match DRNG security strength */
+#define LRNG_DRNG_SECURITY_STRENGTH_BYTES 32
+#define LRNG_DRNG_SECURITY_STRENGTH_BITS (LRNG_DRNG_SECURITY_STRENGTH_BYTES * 8)
+#define LRNG_DRNG_BLOCKSIZE 64 /* Maximum of DRNG block sizes */
+#define LRNG_DRNG_INIT_SEED_SIZE_BITS (LRNG_DRNG_SECURITY_STRENGTH_BITS + \
+ CONFIG_LRNG_SEED_BUFFER_INIT_ADD_BITS)
+#define LRNG_DRNG_INIT_SEED_SIZE_BYTES (LRNG_DRNG_INIT_SEED_SIZE_BITS >> 3)
+
+/*
+ * SP800-90A defines a maximum request size of 1<<16 bytes. The given value is
+ * considered a safer margin.
+ *
+ * This value is allowed to be changed.
+ */
+#define LRNG_DRNG_MAX_REQSIZE (1<<12)
+
+/*
+ * SP800-90A defines a maximum number of requests between reseeds of 2^48.
+ * The given value is considered a much safer margin, balancing requests for
+ * frequent reseeds with the need to conserve entropy. This value MUST NOT be
+ * larger than INT_MAX because it is used in an atomic_t.
+ *
+ * This value is allowed to be changed.
+ */
+#define LRNG_DRNG_RESEED_THRESH (1<<20)
+
+/*
+ * Maximum DRNG generation operations without reseed having full entropy
+ * This value defines the absolute maximum value of DRNG generation operations
+ * without a reseed holding full entropy. LRNG_DRNG_RESEED_THRESH is the
+ * threshold when a new reseed is attempted. But it is possible that this fails
+ * to deliver full entropy. In this case the DRNG will continue to provide data
+ * even though it was not reseeded with full entropy. To avoid in the extreme
+ * case that no reseed is performed for too long, this threshold is enforced.
+ * If that absolute low value is reached, the LRNG is marked as not operational.
+ *
+ * This value is allowed to be changed.
+ */
+#define LRNG_DRNG_MAX_WITHOUT_RESEED (1<<30)
+
+/*
+ * Min required seed entropy is 128 bits covering the minimum entropy
+ * requirement of SP800-131A and the German BSI's TR02102.
+ *
+ * This value is allowed to be changed.
+ */
+#define LRNG_FULL_SEED_ENTROPY_BITS LRNG_DRNG_SECURITY_STRENGTH_BITS
+#define LRNG_MIN_SEED_ENTROPY_BITS 128
+#define LRNG_INIT_ENTROPY_BITS 32
+
+/*
+ * Wakeup value
+ *
+ * This value is allowed to be changed but must not be larger than the
+ * digest size of the hash operation used update the aux_pool.
+ */
+#ifdef CONFIG_CRYPTO_LIB_SHA256
+# define LRNG_ATOMIC_DIGEST_SIZE SHA256_DIGEST_SIZE
+#else
+# define LRNG_ATOMIC_DIGEST_SIZE SHA1_DIGEST_SIZE
+#endif
+#define LRNG_WRITE_WAKEUP_ENTROPY LRNG_ATOMIC_DIGEST_SIZE
+
+/*
+ * If the switching support is configured, we must provide support up to
+ * the largest digest size. Without switching support, we know it is only
+ * the built-in digest size.
+ */
+#ifdef CONFIG_LRNG_DRNG_SWITCH
+# define LRNG_MAX_DIGESTSIZE 64
+#else
+# define LRNG_MAX_DIGESTSIZE LRNG_ATOMIC_DIGEST_SIZE
+#endif
+
+/*
+ * Oversampling factor of IRQ events to obtain
+ * LRNG_DRNG_SECURITY_STRENGTH_BYTES. This factor is used when a
+ * high-resolution time stamp is not available. In this case, jiffies and
+ * register contents are used to fill the entropy pool. These noise sources
+ * are much less entropic than the high-resolution timer. The entropy content
+ * is the entropy content assumed with LRNG_IRQ_ENTROPY_BITS divided by
+ * LRNG_IRQ_OVERSAMPLING_FACTOR.
+ *
+ * This value is allowed to be changed.
+ */
+#define LRNG_IRQ_OVERSAMPLING_FACTOR 10
+
+/* Alignmask that is intended to be identical to CRYPTO_MINALIGN */
+#define LRNG_KCAPI_ALIGN ARCH_KMALLOC_MINALIGN
+
+/*
+ * This definition must provide a buffer that is equal to SHASH_DESC_ON_STACK
+ * as it will be casted into a struct shash_desc.
+ */
+#define LRNG_POOL_SIZE (sizeof(struct shash_desc) + HASH_MAX_DESCSIZE)
+
+/************************ Default DRNG implementation *************************/
+
+extern struct chacha20_state chacha20;
+extern const struct lrng_crypto_cb lrng_cc20_crypto_cb;
+void lrng_cc20_init_state(struct chacha20_state *state);
+
+/********************************** /proc *************************************/
+
+#ifdef CONFIG_SYSCTL
+void lrng_pool_inc_numa_node(void);
+void lrng_proc_update_max_write_thresh(u32 new_digestsize);
+#else
+static inline void lrng_pool_inc_numa_node(void) { }
+static inline void lrng_proc_update_max_write_thresh(u32 new_digestsize) { }
+#endif
+
+/****************************** LRNG interfaces *******************************/
+
+extern u32 lrng_write_wakeup_bits;
+extern int lrng_drng_reseed_max_time;
+
+void lrng_writer_wakeup(void);
+void lrng_init_wakeup(void);
+void lrng_debug_report_seedlevel(const char *name);
+void lrng_process_ready_list(void);
+
+/* External interface to use of the switchable DRBG inside the kernel */
+void get_random_bytes_full(void *buf, int nbytes);
+
+/************************* Jitter RNG Entropy Source **************************/
+
+#ifdef CONFIG_LRNG_JENT
+u32 lrng_get_jent(u8 *outbuf, u32 requested_bits);
+u32 lrng_jent_entropylevel(u32 requested_bits);
+void lrng_jent_es_state(unsigned char *buf, size_t buflen);
+#else /* CONFIG_LRNG_JENT */
+static inline u32 lrng_get_jent(u8 *outbuf, u32 requested_bits) { return 0; }
+static inline u32 lrng_jent_entropylevel(u32 requested_bits) { return 0; }
+static inline void lrng_jent_es_state(unsigned char *buf, size_t buflen) { }
+#endif /* CONFIG_LRNG_JENT */
+
+/************************** CPU-based Entropy Source **************************/
+
+static inline u32 lrng_fast_noise_entropylevel(u32 ent_bits, u32 requested_bits)
+{
+ /* Obtain entropy statement */
+ ent_bits = ent_bits * requested_bits / LRNG_DRNG_SECURITY_STRENGTH_BITS;
+ /* Cap entropy to buffer size in bits */
+ ent_bits = min_t(u32, ent_bits, requested_bits);
+ return ent_bits;
+}
+
+#ifdef CONFIG_LRNG_CPU
+u32 lrng_get_arch(u8 *outbuf, u32 requested_bits);
+u32 lrng_archrandom_entropylevel(u32 requested_bits);
+void lrng_arch_es_state(unsigned char *buf, size_t buflen);
+#else /* CONFIG_LRNG_CPU */
+static inline u32 lrng_get_arch(u8 *outbuf, u32 requested_bits) { return 0; }
+static inline u32 lrng_archrandom_entropylevel(u32 requested_bits) { return 0; }
+static inline void lrng_arch_es_state(unsigned char *buf, size_t buflen) { }
+#endif /* CONFIG_LRNG_CPU */
+
+/************************** Interrupt Entropy Source **************************/
+
+#ifdef CONFIG_LRNG_IRQ
+void lrng_pcpu_reset(void);
+u32 lrng_pcpu_avail_pool_size(void);
+u32 lrng_pcpu_avail_entropy(void);
+int lrng_pcpu_switch_hash(int node,
+ const struct lrng_crypto_cb *new_cb, void *new_hash,
+ const struct lrng_crypto_cb *old_cb);
+u32 lrng_pcpu_pool_hash(u8 *outbuf, u32 requested_bits, bool fully_seeded);
+void lrng_pcpu_array_add_u32(u32 data);
+u32 lrng_gcd_analyze(u32 *history, size_t nelem);
+void lrng_irq_es_state(unsigned char *buf, size_t buflen);
+#else /* CONFIG_LRNG_IRQ */
+static inline void lrng_pcpu_reset(void) { }
+static inline u32 lrng_pcpu_avail_pool_size(void) { return 0; }
+static inline u32 lrng_pcpu_avail_entropy(void) { return 0; }
+static inline int lrng_pcpu_switch_hash(int node,
+ const struct lrng_crypto_cb *new_cb, void *new_hash,
+ const struct lrng_crypto_cb *old_cb)
+{
+ return 0;
+}
+static inline u32 lrng_pcpu_pool_hash(u8 *outbuf, u32 requested_bits,
+ bool fully_seeded)
+{
+ return 0;
+}
+static inline void lrng_pcpu_array_add_u32(u32 data) { }
+static inline void lrng_irq_es_state(unsigned char *buf, size_t buflen) { }
+#endif /* CONFIG_LRNG_IRQ */
+
+/****************************** DRNG processing *******************************/
+
+/* DRNG state handle */
+struct lrng_drng {
+ void *drng; /* DRNG handle */
+ void *hash; /* Hash handle */
+ const struct lrng_crypto_cb *crypto_cb; /* Crypto callbacks */
+ atomic_t requests; /* Number of DRNG requests */
+ atomic_t requests_since_fully_seeded; /* Number DRNG requests since
+ last fully seeded */
+ unsigned long last_seeded; /* Last time it was seeded */
+ bool fully_seeded; /* Is DRNG fully seeded? */
+ bool force_reseed; /* Force a reseed */
+
+ /* Lock write operations on DRNG state, DRNG replacement of crypto_cb */
+ struct mutex lock;
+ spinlock_t spin_lock;
+ /* Lock *hash replacement - always take before DRNG lock */
+ rwlock_t hash_lock;
+};
+
+extern struct mutex lrng_crypto_cb_update;
+
+struct lrng_drng *lrng_drng_init_instance(void);
+struct lrng_drng *lrng_drng_atomic_instance(void);
+
+static __always_inline bool lrng_drng_is_atomic(struct lrng_drng *drng)
+{
+ return (drng->drng == lrng_drng_atomic_instance()->drng);
+}
+
+/* Lock the DRNG */
+static __always_inline void lrng_drng_lock(struct lrng_drng *drng,
+ unsigned long *flags)
+ __acquires(&drng->spin_lock)
+{
+ /* Use spin lock in case the atomic DRNG context is used */
+ if (lrng_drng_is_atomic(drng)) {
+ spin_lock_irqsave(&drng->spin_lock, *flags);
+
+ /*
+ * In case a lock transition happened while we were spinning,
+ * catch this case and use the new lock type.
+ */
+ if (!lrng_drng_is_atomic(drng)) {
+ spin_unlock_irqrestore(&drng->spin_lock, *flags);
+ __acquire(&drng->spin_lock);
+ mutex_lock(&drng->lock);
+ }
+ } else {
+ __acquire(&drng->spin_lock);
+ mutex_lock(&drng->lock);
+ }
+}
+
+/* Unlock the DRNG */
+static __always_inline void lrng_drng_unlock(struct lrng_drng *drng,
+ unsigned long *flags)
+ __releases(&drng->spin_lock)
+{
+ if (lrng_drng_is_atomic(drng)) {
+ spin_unlock_irqrestore(&drng->spin_lock, *flags);
+ } else {
+ mutex_unlock(&drng->lock);
+ __release(&drng->spin_lock);
+ }
+}
+
+void lrng_reset(void);
+void lrng_drngs_init_cc20(bool force_seed);
+bool lrng_sp80090c_compliant(void);
+
+static inline u32 lrng_compress_osr(void)
+{
+ return lrng_sp80090c_compliant() ? CONFIG_LRNG_OVERSAMPLE_ES_BITS : 0;
+}
+
+static inline u32 lrng_reduce_by_osr(u32 entropy_bits)
+{
+ u32 osr_bits = lrng_compress_osr();
+ return (entropy_bits >= osr_bits) ? (entropy_bits - osr_bits) : 0;
+}
+
+bool lrng_get_available(void);
+void lrng_set_available(void);
+void lrng_drng_reset(struct lrng_drng *drng);
+int lrng_drng_get_atomic(u8 *outbuf, u32 outbuflen);
+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 *************************/
+
+enum lrng_external_noise_source {
+ lrng_noise_source_hw,
+ lrng_noise_source_user
+};
+
+void lrng_set_entropy_thresh(u32 new);
+u32 lrng_avail_entropy(void);
+void lrng_reset_state(void);
+
+bool lrng_state_exseed_allow(enum lrng_external_noise_source source);
+void lrng_state_exseed_set(enum lrng_external_noise_source source, bool type);
+bool lrng_state_min_seeded(void);
+bool lrng_state_fully_seeded(void);
+bool lrng_state_operational(void);
+
+int lrng_pool_trylock(void);
+void lrng_pool_unlock(void);
+void lrng_pool_all_numa_nodes_seeded(bool set);
+void lrng_pool_add_entropy(void);
+
+struct entropy_buf {
+ u8 a[LRNG_DRNG_INIT_SEED_SIZE_BYTES];
+ u8 b[LRNG_DRNG_INIT_SEED_SIZE_BYTES];
+ u8 c[LRNG_DRNG_INIT_SEED_SIZE_BYTES];
+ u8 d[LRNG_DRNG_INIT_SEED_SIZE_BYTES];
+ u32 now, a_bits, b_bits, c_bits, d_bits;
+};
+
+bool lrng_fully_seeded(bool fully_seeded, struct entropy_buf *eb);
+void lrng_unset_fully_seeded(struct lrng_drng *drng);
+void lrng_fill_seed_buffer(struct entropy_buf *entropy_buf, u32 requested_bits);
+void lrng_init_ops(struct entropy_buf *eb);
+
+/*********************** Auxiliary Pool Entropy Source ************************/
+
+u32 lrng_avail_aux_entropy(void);
+void lrng_aux_es_state(unsigned char *buf, size_t buflen);
+u32 lrng_get_digestsize(void);
+void lrng_pool_set_entropy(u32 entropy_bits);
+int lrng_aux_switch_hash(const struct lrng_crypto_cb *new_cb, void *new_hash,
+ const struct lrng_crypto_cb *old_cb);
+int lrng_pool_insert_aux(const u8 *inbuf, u32 inbuflen, u32 entropy_bits);
+void lrng_get_backtrack_aux(struct entropy_buf *entropy_buf,
+ u32 requested_bits);
+
+/* Obtain the security strength of the LRNG in bits */
+static inline u32 lrng_security_strength(void)
+{
+ /*
+ * We use a hash to read the entropy in the entropy pool. According to
+ * SP800-90B table 1, the entropy can be at most the digest size.
+ * Considering this together with the last sentence in section 3.1.5.1.2
+ * the security strength of a (approved) hash is equal to its output
+ * size. On the other hand the entropy cannot be larger than the
+ * security strength of the used DRBG.
+ */
+ return min_t(u32, LRNG_FULL_SEED_ENTROPY_BITS, lrng_get_digestsize());
+}
+
+static inline u32 lrng_get_seed_entropy_osr(bool fully_seeded)
+{
+ u32 requested_bits = lrng_security_strength();
+
+ /* Apply oversampling during initialization according to SP800-90C */
+ if (lrng_sp80090c_compliant() && !fully_seeded)
+ requested_bits += CONFIG_LRNG_SEED_BUFFER_INIT_ADD_BITS;
+ return requested_bits;
+}
+
+/************************** Health Test linking code **************************/
+
+enum lrng_health_res {
+ lrng_health_pass, /* Health test passes on time stamp */
+ lrng_health_fail_use, /* Time stamp unhealthy, but mix in */
+ lrng_health_fail_drop /* Time stamp unhealthy, drop it */
+};
+
+#ifdef CONFIG_LRNG_HEALTH_TESTS
+bool lrng_sp80090b_startup_complete(void);
+bool lrng_sp80090b_compliant(void);
+
+enum lrng_health_res lrng_health_test(u32 now_time);
+void lrng_health_disable(void);
+
+#else /* CONFIG_LRNG_HEALTH_TESTS */
+static inline bool lrng_sp80090b_startup_complete(void) { return true; }
+static inline bool lrng_sp80090b_compliant(void) { return false; }
+
+static inline enum lrng_health_res
+lrng_health_test(u32 now_time) { return lrng_health_pass; }
+static inline void lrng_health_disable(void) { }
+#endif /* CONFIG_LRNG_HEALTH_TESTS */
+
+/****************************** Helper code ***********************************/
+
+static inline u32 atomic_read_u32(atomic_t *v)
+{
+ return (u32)atomic_read(v);
+}
+
+/******************** Crypto Primitive Switching Support **********************/
+
+#ifdef CONFIG_LRNG_DRNG_SWITCH
+static inline void lrng_hash_lock(struct lrng_drng *drng, unsigned long *flags)
+{
+ read_lock_irqsave(&drng->hash_lock, *flags);
+}
+
+static inline void lrng_hash_unlock(struct lrng_drng *drng, unsigned long flags)
+{
+ read_unlock_irqrestore(&drng->hash_lock, flags);
+}
+#else /* CONFIG_LRNG_DRNG_SWITCH */
+static inline void lrng_hash_lock(struct lrng_drng *drng, unsigned long *flags)
+{ }
+
+static inline void lrng_hash_unlock(struct lrng_drng *drng, unsigned long flags)
+{ }
+#endif /* CONFIG_LRNG_DRNG_SWITCH */
+
+/*************************** Auxiliary functions ******************************/
+
+void invalidate_batched_entropy(void);
+
+/***************************** Testing code ***********************************/
+
+#ifdef CONFIG_LRNG_RAW_HIRES_ENTROPY
+bool lrng_raw_hires_entropy_store(u32 value);
+#else /* CONFIG_LRNG_RAW_HIRES_ENTROPY */
+static inline bool lrng_raw_hires_entropy_store(u32 value) { return false; }
+#endif /* CONFIG_LRNG_RAW_HIRES_ENTROPY */
+
+#ifdef CONFIG_LRNG_RAW_JIFFIES_ENTROPY
+bool lrng_raw_jiffies_entropy_store(u32 value);
+#else /* CONFIG_LRNG_RAW_JIFFIES_ENTROPY */
+static inline bool lrng_raw_jiffies_entropy_store(u32 value) { return false; }
+#endif /* CONFIG_LRNG_RAW_JIFFIES_ENTROPY */
+
+#ifdef CONFIG_LRNG_RAW_IRQ_ENTROPY
+bool lrng_raw_irq_entropy_store(u32 value);
+#else /* CONFIG_LRNG_RAW_IRQ_ENTROPY */
+static inline bool lrng_raw_irq_entropy_store(u32 value) { return false; }
+#endif /* CONFIG_LRNG_RAW_IRQ_ENTROPY */
+
+#ifdef CONFIG_LRNG_RAW_IRQFLAGS_ENTROPY
+bool lrng_raw_irqflags_entropy_store(u32 value);
+#else /* CONFIG_LRNG_RAW_IRQFLAGS_ENTROPY */
+static inline bool lrng_raw_irqflags_entropy_store(u32 value) { return false; }
+#endif /* CONFIG_LRNG_RAW_IRQFLAGS_ENTROPY */
+
+#ifdef CONFIG_LRNG_RAW_RETIP_ENTROPY
+bool lrng_raw_retip_entropy_store(u32 value);
+#else /* CONFIG_LRNG_RAW_RETIP_ENTROPY */
+static inline bool lrng_raw_retip_entropy_store(u32 value) { return false; }
+#endif /* CONFIG_LRNG_RAW_RETIP_ENTROPY */
+
+#ifdef CONFIG_LRNG_RAW_REGS_ENTROPY
+bool lrng_raw_regs_entropy_store(u32 value);
+#else /* CONFIG_LRNG_RAW_REGS_ENTROPY */
+static inline bool lrng_raw_regs_entropy_store(u32 value) { return false; }
+#endif /* CONFIG_LRNG_RAW_REGS_ENTROPY */
+
+#ifdef CONFIG_LRNG_RAW_ARRAY
+bool lrng_raw_array_entropy_store(u32 value);
+#else /* CONFIG_LRNG_RAW_ARRAY */
+static inline bool lrng_raw_array_entropy_store(u32 value) { return false; }
+#endif /* CONFIG_LRNG_RAW_ARRAY */
+
+#ifdef CONFIG_LRNG_IRQ_PERF
+bool lrng_perf_time(u32 start);
+#else /* CONFIG_LRNG_IRQ_PERF */
+static inline bool lrng_perf_time(u32 start) { return false; }
+#endif /*CONFIG_LRNG_IRQ_PERF */
+
+#endif /* _LRNG_INTERNAL_H */
diff --git a/include/linux/lrng.h b/include/linux/lrng.h
new file mode 100644
index 000000000000..3e8f93b53c84
--- /dev/null
+++ b/include/linux/lrng.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2018 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#ifndef _LRNG_H
+#define _LRNG_H
+
+#include <crypto/hash.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+
+/*
+ * struct lrng_crypto_cb - cryptographic callback functions
+ * @lrng_drng_name Name of DRNG
+ * @lrng_hash_name Name of Hash used for reading entropy pool
+ * @lrng_drng_alloc: Allocate DRNG -- the provided integer should be
+ * used for sanity checks.
+ * return: allocated data structure or PTR_ERR on
+ * error
+ * @lrng_drng_dealloc: Deallocate DRNG
+ * @lrng_drng_seed_helper: Seed the DRNG with data of arbitrary length
+ * drng: is pointer to data structure allocated
+ * with lrng_drng_alloc
+ * return: >= 0 on success, < 0 on error
+ * @lrng_drng_generate_helper: Generate random numbers from the DRNG with
+ * arbitrary length
+ * @lrng_hash_alloc: Allocate the hash for reading the entropy pool
+ * return: allocated data structure (NULL is
+ * success too) or ERR_PTR on error
+ * @lrng_hash_dealloc: Deallocate Hash
+ * @lrng_hash_digestsize: Return the digestsize for the used hash to read
+ * out entropy pool
+ * hash: is pointer to data structure allocated
+ * with lrng_hash_alloc
+ * return: size of digest of hash in bytes
+ * @lrng_hash_init: Initialize hash
+ * hash: is pointer to data structure allocated
+ * with lrng_hash_alloc
+ * return: 0 on success, < 0 on error
+ * @lrng_hash_update: Update hash operation
+ * hash: is pointer to data structure allocated
+ * with lrng_hash_alloc
+ * return: 0 on success, < 0 on error
+ * @lrng_hash_final Final hash operation
+ * hash: is pointer to data structure allocated
+ * with lrng_hash_alloc
+ * return: 0 on success, < 0 on error
+ * @lrng_hash_desc_zero Zeroization of hash state buffer
+ *
+ * Assumptions:
+ *
+ * 1. Hash operation will not sleep
+ * 2. The hash' volatile state information is provided with *shash by caller.
+ */
+struct lrng_crypto_cb {
+ const char *(*lrng_drng_name)(void);
+ const char *(*lrng_hash_name)(void);
+ void *(*lrng_drng_alloc)(u32 sec_strength);
+ void (*lrng_drng_dealloc)(void *drng);
+ int (*lrng_drng_seed_helper)(void *drng, const u8 *inbuf, u32 inbuflen);
+ int (*lrng_drng_generate_helper)(void *drng, u8 *outbuf, u32 outbuflen);
+ void *(*lrng_hash_alloc)(void);
+ void (*lrng_hash_dealloc)(void *hash);
+ u32 (*lrng_hash_digestsize)(void *hash);
+ int (*lrng_hash_init)(struct shash_desc *shash, void *hash);
+ int (*lrng_hash_update)(struct shash_desc *shash, const u8 *inbuf,
+ u32 inbuflen);
+ int (*lrng_hash_final)(struct shash_desc *shash, u8 *digest);
+ void (*lrng_hash_desc_zero)(struct shash_desc *shash);
+};
+
+/* Register cryptographic backend */
+#ifdef CONFIG_LRNG_DRNG_SWITCH
+int lrng_set_drng_cb(const struct lrng_crypto_cb *cb);
+#else /* CONFIG_LRNG_DRNG_SWITCH */
+static inline int
+lrng_set_drng_cb(const struct lrng_crypto_cb *cb) { return -EOPNOTSUPP; }
+#endif /* CONFIG_LRNG_DRNG_SWITCH */
+
+#endif /* _LRNG_H */
--
2.31.1





2021-11-21 16:52:09

by Stephan Müller

[permalink] [raw]
Subject: [PATCH v43 06/15] LRNG - add switchable DRNG support

The DRNG switch support allows replacing the DRNG mechanism of the
LRNG. The switching support rests on the interface definition of
include/linux/lrng.h. A new DRNG is implemented by filling in the
interface defined in this header file.

In addition to the DRNG, the extension also has to provide a hash
implementation that is used to hash the entropy pool for random number
extraction.

Note: It is permissible to implement a DRNG whose operations may sleep.
However, the hash function must not sleep.

The switchable DRNG support allows replacing the DRNG at runtime.
However, only one DRNG extension is allowed to be loaded at any given
time. Before replacing it with another DRNG implementation, the possibly
existing DRNG extension must be unloaded.

The switchable DRNG extension activates the new DRNG during load time.
It is expected, however, that such a DRNG switch would be done only once
by an administrator to load the intended DRNG implementation.

It is permissible to compile DRNG extensions either as kernel modules or
statically. The initialization of the DRNG extension should be performed
with a late_initcall to ensure the extension is available when user
space starts but after all other initialization completed.
The initialization is performed by registering the function call data
structure with the lrng_set_drng_cb function. In order to unload the
DRNG extension, lrng_set_drng_cb must be invoked with the NULL
parameter.

The DRNG extension should always provide a security strength that is at
least as strong as LRNG_DRNG_SECURITY_STRENGTH_BITS.

The hash extension must not sleep and must not maintain a separate
state.

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]>
Reviewed-by: Alexander Lobakin <[email protected]>
Tested-by: 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]>
Tested-by: Jirka Hladky <[email protected]>
Reviewed-by: Jirka Hladky <[email protected]>
Signed-off-by: Stephan Mueller <[email protected]>
---
drivers/char/lrng/Kconfig | 7 +
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_switch.c | 226 ++++++++++++++++++++++++++++++++
3 files changed, 234 insertions(+)
create mode 100644 drivers/char/lrng/lrng_switch.c

diff --git a/drivers/char/lrng/Kconfig b/drivers/char/lrng/Kconfig
index d3abdfa28493..9fea73b72f88 100644
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -255,4 +255,11 @@ config LRNG_CPU_ENTROPY_RATE

endmenu # "Entropy Source Configuration"

+menuconfig LRNG_DRNG_SWITCH
+ bool "Support DRNG runtime switching"
+ help
+ The Linux RNG per default uses a ChaCha20 DRNG that is
+ accessible via the external interfaces. With this configuration
+ option other DRNGs can be selected and loaded at runtime.
+
endif # LRNG
diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile
index 1e18e2c1016b..2202aa78b63f 100644
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_LRNG_IRQ) += lrng_es_irq.o
obj-$(CONFIG_SYSCTL) += lrng_proc.o
obj-$(CONFIG_NUMA) += lrng_numa.o
obj-$(CONFIG_LRNG_CPU) += lrng_es_archrandom.o
+obj-$(CONFIG_LRNG_DRNG_SWITCH) += lrng_switch.o
diff --git a/drivers/char/lrng/lrng_switch.c b/drivers/char/lrng/lrng_switch.c
new file mode 100644
index 000000000000..5fce40149911
--- /dev/null
+++ b/drivers/char/lrng/lrng_switch.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG DRNG switching support
+ *
+ * Copyright (C) 2016 - 2021, Stephan Mueller <[email protected]>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/lrng.h>
+
+#include "lrng_internal.h"
+
+static int lrng_drng_switch(struct lrng_drng *drng_store,
+ const struct lrng_crypto_cb *cb, int node)
+{
+ const struct lrng_crypto_cb *old_cb;
+ unsigned long flags = 0, flags2 = 0;
+ int ret;
+ u8 seed[LRNG_DRNG_SECURITY_STRENGTH_BYTES];
+ void *new_drng = cb->lrng_drng_alloc(LRNG_DRNG_SECURITY_STRENGTH_BYTES);
+ void *old_drng, *new_hash, *old_hash;
+ u32 current_security_strength;
+ bool sl = false, reset_drng = !lrng_get_available();
+
+ if (IS_ERR(new_drng)) {
+ pr_warn("could not allocate new DRNG for NUMA node %d (%ld)\n",
+ node, PTR_ERR(new_drng));
+ return PTR_ERR(new_drng);
+ }
+
+ new_hash = cb->lrng_hash_alloc();
+ if (IS_ERR(new_hash)) {
+ pr_warn("could not allocate new LRNG pool hash (%ld)\n",
+ PTR_ERR(new_hash));
+ cb->lrng_drng_dealloc(new_drng);
+ return PTR_ERR(new_hash);
+ }
+
+ if (cb->lrng_hash_digestsize(new_hash) > LRNG_MAX_DIGESTSIZE) {
+ pr_warn("digest size of newly requested hash too large\n");
+ cb->lrng_hash_dealloc(new_hash);
+ cb->lrng_drng_dealloc(new_drng);
+ return -EINVAL;
+ }
+
+ current_security_strength = lrng_security_strength();
+ lrng_drng_lock(drng_store, &flags);
+
+ /*
+ * Pull from existing DRNG to seed new DRNG regardless of seed status
+ * of old DRNG -- the entropy state for the DRNG is left unchanged which
+ * implies that als the new DRNG is reseeded when deemed necessary. This
+ * seeding of the new DRNG shall only ensure that the new DRNG has the
+ * same entropy as the old DRNG.
+ */
+ ret = drng_store->crypto_cb->lrng_drng_generate_helper(
+ drng_store->drng, seed, sizeof(seed));
+ lrng_drng_unlock(drng_store, &flags);
+
+ if (ret < 0) {
+ reset_drng = true;
+ pr_warn("getting random data from DRNG failed for NUMA node %d (%d)\n",
+ node, ret);
+ } else {
+ /* seed new DRNG with data */
+ ret = cb->lrng_drng_seed_helper(new_drng, seed, ret);
+ memzero_explicit(seed, sizeof(seed));
+ if (ret < 0) {
+ reset_drng = true;
+ pr_warn("seeding of new DRNG failed for NUMA node %d (%d)\n",
+ node, ret);
+ } else {
+ pr_debug("seeded new DRNG of NUMA node %d instance from old DRNG instance\n",
+ node);
+ }
+ }
+
+ mutex_lock(&drng_store->lock);
+ write_lock_irqsave(&drng_store->hash_lock, flags2);
+ /*
+ * If we switch the DRNG from the initial ChaCha20 DRNG to something
+ * else, there is a lock transition from spin lock to mutex (see
+ * lrng_drng_is_atomic and how the lock is taken in lrng_drng_lock).
+ * Thus, we need to take both locks during the transition phase.
+ */
+ if (lrng_drng_is_atomic(drng_store)) {
+ spin_lock_irqsave(&drng_store->spin_lock, flags);
+ sl = true;
+ } else {
+ __acquire(&drng_store->spin_lock);
+ }
+
+ /* Trigger the switch of the aux entropy pool for current node. */
+ if (drng_store == lrng_drng_init_instance()) {
+ ret = lrng_aux_switch_hash(cb, new_hash, drng_store->crypto_cb);
+ if (ret)
+ goto err;
+ }
+
+ /* Trigger the switch of the per-CPU entropy pools for current node. */
+ ret = lrng_pcpu_switch_hash(node, cb, new_hash, drng_store->crypto_cb);
+ if (ret) {
+ /* Switch the crypto operation back to be consistent */
+ WARN_ON(lrng_aux_switch_hash(drng_store->crypto_cb,
+ drng_store->hash, cb));
+ } else {
+ if (reset_drng)
+ lrng_drng_reset(drng_store);
+
+ old_drng = drng_store->drng;
+ old_cb = drng_store->crypto_cb;
+ drng_store->drng = new_drng;
+ drng_store->crypto_cb = cb;
+
+ old_hash = drng_store->hash;
+ drng_store->hash = new_hash;
+ pr_info("Entropy pool read-hash allocated for DRNG for NUMA node %d\n",
+ node);
+
+ /* Reseed if previous LRNG security strength was insufficient */
+ if (current_security_strength < lrng_security_strength())
+ drng_store->force_reseed = true;
+
+ /* Force oversampling seeding as we initialize DRNG */
+ if (IS_ENABLED(CONFIG_LRNG_OVERSAMPLE_ENTROPY_SOURCES))
+ lrng_unset_fully_seeded(drng_store);
+
+ if (lrng_state_min_seeded())
+ lrng_set_entropy_thresh(lrng_get_seed_entropy_osr(
+ drng_store->fully_seeded));
+
+ /* ChaCha20 serves as atomic instance left untouched. */
+ if (old_drng != &chacha20) {
+ old_cb->lrng_drng_dealloc(old_drng);
+ old_cb->lrng_hash_dealloc(old_hash);
+ }
+
+ pr_info("DRNG of NUMA node %d switched\n", node);
+ }
+
+err:
+ if (sl)
+ spin_unlock_irqrestore(&drng_store->spin_lock, flags);
+ else
+ __release(&drng_store->spin_lock);
+ write_unlock_irqrestore(&drng_store->hash_lock, flags2);
+ mutex_unlock(&drng_store->lock);
+
+ return ret;
+}
+
+/*
+ * Switch the existing DRNG instances with new using the new crypto callbacks.
+ * The caller must hold the lrng_crypto_cb_update lock.
+ */
+static int lrng_drngs_switch(const struct lrng_crypto_cb *cb)
+{
+ struct lrng_drng **lrng_drng = lrng_drng_instances();
+ struct lrng_drng *lrng_drng_init = lrng_drng_init_instance();
+ int ret = 0;
+
+ /* Update DRNG */
+ if (lrng_drng) {
+ u32 node;
+
+ for_each_online_node(node) {
+ if (lrng_drng[node])
+ ret = lrng_drng_switch(lrng_drng[node], cb,
+ node);
+ }
+ } else {
+ ret = lrng_drng_switch(lrng_drng_init, cb, 0);
+ }
+
+ if (!ret)
+ lrng_set_available();
+
+ return 0;
+}
+
+/*
+ * lrng_set_drng_cb - Register new cryptographic callback functions for DRNG
+ * The registering implies that all old DRNG states are replaced with new
+ * DRNG states.
+ *
+ * @cb: Callback functions to be registered -- if NULL, use the default
+ * callbacks pointing to the ChaCha20 DRNG.
+ *
+ * Return:
+ * * 0 on success
+ * * < 0 on error
+ */
+int lrng_set_drng_cb(const struct lrng_crypto_cb *cb)
+{
+ struct lrng_drng *lrng_drng_init = lrng_drng_init_instance();
+ int ret;
+
+ if (!cb)
+ cb = &lrng_cc20_crypto_cb;
+
+ mutex_lock(&lrng_crypto_cb_update);
+
+ /*
+ * If a callback other than the default is set, allow it only to be
+ * set back to the default callback. This ensures that multiple
+ * different callbacks can be registered at the same time. If a
+ * callback different from the current callback and the default
+ * callback shall be set, the current callback must be deregistered
+ * (e.g. the kernel module providing it must be unloaded) and the new
+ * implementation can be registered.
+ */
+ if ((cb != &lrng_cc20_crypto_cb) &&
+ (lrng_drng_init->crypto_cb != &lrng_cc20_crypto_cb)) {
+ pr_warn("disallow setting new cipher callbacks, unload the old callbacks first!\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = lrng_drngs_switch(cb);
+
+out:
+ mutex_unlock(&lrng_crypto_cb_update);
+ return ret;
+}
+EXPORT_SYMBOL(lrng_set_drng_cb);
--
2.31.1





2021-11-21 17:24:08

by Joe Perches

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Sun, 2021-11-21 at 17:40 +0100, Stephan M?ller wrote:
> In an effort to provide a flexible implementation for a random number
> generator that also delivers entropy during early boot time, allows
> replacement of the deterministic random number generation mechanism,
> implement the various components in separate code for easier
> maintenance, and provide compliance to SP800-90[A|B|C], introduce
> the Linux Random Number Generator (LRNG) framework.
[]
> diff --git a/MAINTAINERS b/MAINTAINERS
[]
> @@ -10817,6 +10817,13 @@ F: Documentation/litmus-tests/
> F: Documentation/memory-barriers.txt
> F: tools/memory-model/
>
> +LINUX RANDOM NUMBER GENERATOR (LRNG) DRIVER
> +M: Stephan Mueller <[email protected]>
> +S: Maintained
> +W: https://www.chronox.de/lrng.html
> +F: drivers/char/lrng/*

Are you specifically intending _not_ to maintain any files in
any possible subdirectories of this directory?

If not, this should be

F: drivers/char/lrng/

> +F: include/linux/lrng.h
> +

Trivia and additionally:

Maybe run the patch series through scripts/checkpatch.pl --strict and
see if you want to change anything.



2021-11-21 22:48:02

by Jason A. Donenfeld

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Hi Stephan,

You've posted it again, and yet I still believe this is not the
correct design or direction. I do not think the explicit goal of
extended configurability ("flexibility") or the explicit goal of being
FIPS compatible represent good directions, and I think this introduces
new problems rather than solving any existing ones. While there are
ways the current RNG could or even should be improved -- or rewritten
-- this approach is still not that, no matter how many times you post
it. It is almost as though you hope this somehow gets accepted through
a general apathy that might develop by the 1000th revision, when
cranks like me and others no longer have the motivation to keep
responding with the same thing. But here we are again.

My own experience pushing something that didn't have substantial
enough buy-in from existing maintainers (the Zinc crypto library)
ultimately led me to stop pushing in order to not alienate folks, step
back, and listen a bit. Eventually somebody reached out to work with
me (Ard) and we submitted a good compromise collaboration that we all
generally felt better about. In this case, your cryptographic design
tastes are sufficiently divergent from mine that I'm not sure how far
such a thing would go, but it also seems to me that continually
pushing the same thing over and over isn't winning you any points here
either. Submission by attrition is not an outcome anybody should want.

Sorry to be so blunt. It's just that my, "I don't like this" is the
same as it was the last time, and I'm not aware of anything
significant in that area changing this time.

Jason

2021-11-22 05:36:10

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Am Sonntag, 21. November 2021, 23:42:33 CET schrieb Jason A. Donenfeld:

Hi Jason,

> Hi Stephan,
>
> You've posted it again, and yet I still believe this is not the
> correct design or direction. I do not think the explicit goal of
> extended configurability ("flexibility") or the explicit goal of being
> FIPS compatible represent good directions, and I think this introduces
> new problems rather than solving any existing ones.

The members from the Linux distributions that are on copy on this may tell you
a different story. They all developed their own downstream patches to somehow
add the flexibility that is needed for them. So, we have a great deal of
fragmentation at the resting-foundation of Linux cryptography.

Then the implementation of the flexibility in the LRNG is done such that
irrespecitve of which options are selected, the LRNG operates always at a
secure state.


> While there are
> ways the current RNG could or even should be improved -- or rewritten
> -- this approach is still not that, no matter how many times you post
> it. It is almost as though you hope this somehow gets accepted through
> a general apathy that might develop by the 1000th revision, when
> cranks like me and others no longer have the motivation to keep
> responding with the same thing. But here we are again.
>
> My own experience pushing something that didn't have substantial
> enough buy-in from existing maintainers (the Zinc crypto library)
> ultimately led me to stop pushing in order to not alienate folks, step
> back, and listen a bit. Eventually somebody reached out to work with
> me (Ard) and we submitted a good compromise collaboration that we all
> generally felt better about. In this case, your cryptographic design
> tastes are sufficiently divergent from mine that I'm not sure how far
> such a thing would go, but it also seems to me that continually
> pushing the same thing over and over isn't winning you any points here
> either. Submission by attrition is not an outcome anybody should want.
>
> Sorry to be so blunt. It's just that my, "I don't like this" is the
> same as it was the last time, and I'm not aware of anything
> significant in that area changing this time.

I have received numerous technical comments from various Linux developers. All
were integrated. None of these comments hinted to requestes in changing the
design.

That said, even when you refer to already suggested architectural differnces,
I yet have to first receive them,
>
> Jason


Ciao
Stephan



2021-11-22 06:02:27

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Nov 22, 2021 at 06:34:43AM +0100, Stephan Mueller wrote:
> Am Sonntag, 21. November 2021, 23:42:33 CET schrieb Jason A. Donenfeld:
>
> Hi Jason,
>
> > Hi Stephan,
> >
> > You've posted it again, and yet I still believe this is not the
> > correct design or direction. I do not think the explicit goal of
> > extended configurability ("flexibility") or the explicit goal of being
> > FIPS compatible represent good directions, and I think this introduces
> > new problems rather than solving any existing ones.
>
> The members from the Linux distributions that are on copy on this may tell you
> a different story. They all developed their own downstream patches to somehow
> add the flexibility that is needed for them. So, we have a great deal of
> fragmentation at the resting-foundation of Linux cryptography.

What distros specifically have patches in their kernels that do
different things to the random code path? Do you have pointers to those
patches anywhere? Why have the distros not submitted their changes
upstream?

thanks,

greg k-h

2021-11-22 06:43:32

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Am Montag, 22. November 2021, 07:02:14 CET schrieb Greg Kroah-Hartman:

Hi Greg,

> On Mon, Nov 22, 2021 at 06:34:43AM +0100, Stephan Mueller wrote:
> > Am Sonntag, 21. November 2021, 23:42:33 CET schrieb Jason A. Donenfeld:
> >
> > Hi Jason,
> >
> > > Hi Stephan,
> > >
> > > You've posted it again, and yet I still believe this is not the
> > > correct design or direction. I do not think the explicit goal of
> > > extended configurability ("flexibility") or the explicit goal of being
> > > FIPS compatible represent good directions, and I think this introduces
> > > new problems rather than solving any existing ones.
> >
> > The members from the Linux distributions that are on copy on this may tell
> > you a different story. They all developed their own downstream patches to
> > somehow add the flexibility that is needed for them. So, we have a great
> > deal of fragmentation at the resting-foundation of Linux cryptography.
>
> What distros specifically have patches in their kernels that do
> different things to the random code path? Do you have pointers to those
> patches anywhere? Why have the distros not submitted their changes
> upstream?

I will leave the representatives from the distros to chime in and point to
these patches.

Yet, these changes are commonly a band-aid only that have some additional
drawbacks. Bottom line, there is no appropriate way with the current code to
allow vendors what they want to achieve. One hint to what changes vendors are
attempting can be found in [1] slide 20.

[1] https://www.chronox.de/lrng/doc/lrng_presentation_v43.pdf
>
> thanks,
>
> greg k-h


Ciao
Stephan



2021-11-22 06:55:41

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Nov 22, 2021 at 07:42:02AM +0100, Stephan Mueller wrote:
> Am Montag, 22. November 2021, 07:02:14 CET schrieb Greg Kroah-Hartman:
>
> Hi Greg,
>
> > On Mon, Nov 22, 2021 at 06:34:43AM +0100, Stephan Mueller wrote:
> > > Am Sonntag, 21. November 2021, 23:42:33 CET schrieb Jason A. Donenfeld:
> > >
> > > Hi Jason,
> > >
> > > > Hi Stephan,
> > > >
> > > > You've posted it again, and yet I still believe this is not the
> > > > correct design or direction. I do not think the explicit goal of
> > > > extended configurability ("flexibility") or the explicit goal of being
> > > > FIPS compatible represent good directions, and I think this introduces
> > > > new problems rather than solving any existing ones.
> > >
> > > The members from the Linux distributions that are on copy on this may tell
> > > you a different story. They all developed their own downstream patches to
> > > somehow add the flexibility that is needed for them. So, we have a great
> > > deal of fragmentation at the resting-foundation of Linux cryptography.
> >
> > What distros specifically have patches in their kernels that do
> > different things to the random code path? Do you have pointers to those
> > patches anywhere? Why have the distros not submitted their changes
> > upstream?
>
> I will leave the representatives from the distros to chime in and point to
> these patches.

Then why not work with the distros to get these changes merged into the
kernel tree? They know that keeping things out-of-the-tree costs them
time and money, so why are they keeping them there?

I recommend getting the distros to chime in on what their requirements
are for the random code would probably be best as they are the ones that
take on the "random fips requirement of the day" more than anyone else.

> Yet, these changes are commonly a band-aid only that have some additional
> drawbacks. Bottom line, there is no appropriate way with the current code to
> allow vendors what they want to achieve. One hint to what changes vendors are
> attempting can be found in [1] slide 20.

What exactly do vendors "want to achieve"? Where are they saying this?

> [1] https://www.chronox.de/lrng/doc/lrng_presentation_v43.pdf

I see nothing on that slide that mentions actual requirements other than
"the current code does not match this random government regulation".

Please provide valid reasons, from distros.

thanks,

greg k-h

2021-11-22 07:10:22

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v43 05/15] LRNG - CPU entropy source

Hi "Stephan,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on char-misc/char-misc-testing]
[also build test WARNING on herbert-cryptodev-2.6/master herbert-crypto-2.6/master v5.16-rc2 next-20211118]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url: https://github.com/0day-ci/linux/commits/Stephan-M-ller/dev-random-a-new-approach/20211122-005114
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git f4d77525679e289d4976ca03b620ac4cc5403205
config: alpha-allyesconfig (attached as .config)
compiler: alpha-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/4db9c892c1827b896be5479eeb9cc4ac0d0a87b5
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Stephan-M-ller/dev-random-a-new-approach/20211122-005114
git checkout 4db9c892c1827b896be5479eeb9cc4ac0d0a87b5
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=alpha

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>

All warnings (new ones prefixed by >>):

>> drivers/char/lrng/lrng_es_archrandom.c:81:1: warning: 'inline' is not at beginning of declaration [-Wold-style-declaration]
81 | static u32 inline lrng_get_arch_data_compress(u8 *outbuf, u32 requested_bits,
| ^~~~~~


vim +/inline +81 drivers/char/lrng/lrng_es_archrandom.c

80
> 81 static u32 inline lrng_get_arch_data_compress(u8 *outbuf, u32 requested_bits,
82 u32 data_multiplier)
83 {
84 SHASH_DESC_ON_STACK(shash, NULL);
85 const struct lrng_crypto_cb *crypto_cb;
86 struct lrng_drng *drng = lrng_drng_init_instance();
87 unsigned long flags;
88 u32 ent_bits = 0, i, partial_bits = 0,
89 full_bits = requested_bits * data_multiplier;
90 void *hash;
91
92 /* Calculate oversampling for SP800-90C */
93 if (lrng_sp80090c_compliant()) {
94 /* Complete amount of bits to be pulled */
95 full_bits += CONFIG_LRNG_OVERSAMPLE_ES_BITS * data_multiplier;
96 /* Full blocks that will be pulled */
97 data_multiplier = full_bits / requested_bits;
98 /* Partial block in bits to be pulled */
99 partial_bits = full_bits - (data_multiplier * requested_bits);
100 }
101
102 lrng_hash_lock(drng, &flags);
103 crypto_cb = drng->crypto_cb;
104 hash = drng->hash;
105
106 if (crypto_cb->lrng_hash_init(shash, hash))
107 goto out;
108
109 /* Hash all data from the CPU entropy source */
110 for (i = 0; i < data_multiplier; i++) {
111 ent_bits = lrng_get_arch_data(outbuf, requested_bits);
112 if (!ent_bits)
113 goto out;
114
115 if (crypto_cb->lrng_hash_update(shash, outbuf, ent_bits >> 3))
116 goto err;
117 }
118
119 /* Hash partial block, if applicable */
120 ent_bits = lrng_get_arch_data(outbuf, partial_bits);
121 if (ent_bits &&
122 crypto_cb->lrng_hash_update(shash, outbuf, ent_bits >> 3))
123 goto err;
124
125 pr_debug("pulled %u bits from CPU RNG entropy source\n", full_bits);
126
127 /* Generate the compressed data to be returned to the caller */
128 ent_bits = crypto_cb->lrng_hash_digestsize(hash) << 3;
129 if (requested_bits < ent_bits) {
130 u8 digest[LRNG_MAX_DIGESTSIZE];
131
132 if (crypto_cb->lrng_hash_final(shash, digest))
133 goto err;
134
135 /* Truncate output data to requested size */
136 memcpy(outbuf, digest, requested_bits >> 3);
137 memzero_explicit(digest, crypto_cb->lrng_hash_digestsize(hash));
138 ent_bits = requested_bits;
139 } else {
140 if (crypto_cb->lrng_hash_final(shash, outbuf))
141 goto err;
142 }
143
144 out:
145 crypto_cb->lrng_hash_desc_zero(shash);
146 lrng_hash_unlock(drng, flags);
147 return ent_bits;
148
149 err:
150 ent_bits = 0;
151 goto out;
152 }
153

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (4.40 kB)
.config.gz (68.48 kB)
Download all attachments

2021-11-22 10:33:57

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Hi "Stephan,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on char-misc/char-misc-testing]
[also build test ERROR on herbert-cryptodev-2.6/master herbert-crypto-2.6/master v5.16-rc2 next-20211118]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url: https://github.com/0day-ci/linux/commits/Stephan-M-ller/dev-random-a-new-approach/20211122-005114
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git f4d77525679e289d4976ca03b620ac4cc5403205
config: mips-allmodconfig (attached as .config)
compiler: mips-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/dccd6203b45303ba2985de5a22238809655b48c4
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Stephan-M-ller/dev-random-a-new-approach/20211122-005114
git checkout dccd6203b45303ba2985de5a22238809655b48c4
# save the attached .config to linux build tree
mkdir build_dir
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=mips SHELL=/bin/bash drivers/char/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>

All errors (new ones prefixed by >>):

>> drivers/char/lrng/lrng_chacha20.c:32:8: error: structure variable 'chacha20' with 'latent_entropy' attribute has a non-integer field 'block'
32 | struct chacha20_state chacha20 __latent_entropy;
| ^~~~~~~~~~~~~~


vim +32 drivers/char/lrng/lrng_chacha20.c

26
27 /*
28 * Have a static memory blocks for the ChaCha20 DRNG instance to avoid calling
29 * kmalloc too early in the boot cycle. For subsequent allocation requests,
30 * such as per-NUMA-node DRNG instances, kmalloc will be used.
31 */
> 32 struct chacha20_state chacha20 __latent_entropy;
33

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (2.25 kB)
.config.gz (70.75 kB)
Download all attachments

2021-11-22 11:48:38

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Am Montag, 22. November 2021, 11:33:26 CET schrieb kernel test robot:

Hi,

> All errors (new ones prefixed by >>):
> >> drivers/char/lrng/lrng_chacha20.c:32:8: error: structure variable
> >> 'chacha20' with 'latent_entropy' attribute has a non-integer field
> >> 'block'
> 32 | struct chacha20_state chacha20 __latent_entropy;
>
> | ^~~~~~~~~~~~~~
>
> vim +32 drivers/char/lrng/lrng_chacha20.c

Thanks for the notification.

I think this is a false-positive discussed before. __latent_entropy is
seemingly allowed for an entire linear buffer as seen in the declaration of
the variable input_pool_data in driver/char/random.c which is an array of u32.

The struct chacha20_state is a linear buffer of u32 words.

struct chacha20_block {
u32 constants[4];
union {
u32 u[CHACHA_KEY_SIZE_WORDS];
u8 b[CHACHA_KEY_SIZE];
} key;
u32 counter;
u32 nonce[3];
};

Therefore it should be identical to the aforementioned example. The
__latent_entropy marker therefore seems to be appropriate for this structure.

Ciao
Stephan




2021-11-22 11:49:50

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH v43 05/15] LRNG - CPU entropy source

Am Montag, 22. November 2021, 08:09:59 CET schrieb kernel test robot:

Hi,

> All warnings (new ones prefixed by >>):
> >> drivers/char/lrng/lrng_es_archrandom.c:81:1: warning: 'inline' is not at
> >> beginning of declaration [-Wold-style-declaration]
> 81 | static u32 inline lrng_get_arch_data_compress(u8 *outbuf, u32
> requested_bits,
> | ^~~~~~

Thank you for the note, this will be fixed.

Ciao
Stephan



2021-11-22 14:59:09

by Simo Sorce

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Jason,
have you previously produced a list of reasoned concerns with this
patchset and direction?

This specific email is not really useful to me to understand the
concerns as it does not contain actionable suggestion or critique.

I personally find the direction fine, and with my distribution hat on I
can say that FIPS is essential for us and any design must include an
option to be FIPS certifiable.

As NIST keeps improving their testing capabilities and rigorous
cryptographic design of the CSPRNGs as well as entropy sources the
kernel must also adapt.

Stephan is providing a path forward, and I haven't seen any other
proposal, let alone code, that provide improvements in this area.
I am pretty sure the design can be improved if there is detailed and
actionable feedback on what to change.

I hope the path forward can be one of collaboration rather then mere
opposition.

HTH,
Simo.

On Sun, 2021-11-21 at 23:42 +0100, Jason A. Donenfeld wrote:
> Hi Stephan,
>
> You've posted it again, and yet I still believe this is not the
> correct design or direction. I do not think the explicit goal of
> extended configurability ("flexibility") or the explicit goal of being
> FIPS compatible represent good directions, and I think this introduces
> new problems rather than solving any existing ones. While there are
> ways the current RNG could or even should be improved -- or rewritten
> -- this approach is still not that, no matter how many times you post
> it. It is almost as though you hope this somehow gets accepted through
> a general apathy that might develop by the 1000th revision, when
> cranks like me and others no longer have the motivation to keep
> responding with the same thing. But here we are again.
>
> My own experience pushing something that didn't have substantial
> enough buy-in from existing maintainers (the Zinc crypto library)
> ultimately led me to stop pushing in order to not alienate folks, step
> back, and listen a bit. Eventually somebody reached out to work with
> me (Ard) and we submitted a good compromise collaboration that we all
> generally felt better about. In this case, your cryptographic design
> tastes are sufficiently divergent from mine that I'm not sure how far
> such a thing would go, but it also seems to me that continually
> pushing the same thing over and over isn't winning you any points here
> either. Submission by attrition is not an outcome anybody should want.
>
> Sorry to be so blunt. It's just that my, "I don't like this" is the
> same as it was the last time, and I'm not aware of anything
> significant in that area changing this time.
>
> Jason
>

--
Simo Sorce
RHEL Crypto Team
Red Hat, Inc





2021-11-22 15:09:12

by Simo Sorce

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, 2021-11-22 at 07:55 +0100, Greg Kroah-Hartman wrote:
> On Mon, Nov 22, 2021 at 07:42:02AM +0100, Stephan Mueller wrote:
> > Am Montag, 22. November 2021, 07:02:14 CET schrieb Greg Kroah-Hartman:
> >
> > Hi Greg,
> >
> > > On Mon, Nov 22, 2021 at 06:34:43AM +0100, Stephan Mueller wrote:
> > > > Am Sonntag, 21. November 2021, 23:42:33 CET schrieb Jason A. Donenfeld:
> > > >
> > > > Hi Jason,
> > > >
> > > > > Hi Stephan,
> > > > >
> > > > > You've posted it again, and yet I still believe this is not the
> > > > > correct design or direction. I do not think the explicit goal of
> > > > > extended configurability ("flexibility") or the explicit goal of being
> > > > > FIPS compatible represent good directions, and I think this introduces
> > > > > new problems rather than solving any existing ones.
> > > >
> > > > The members from the Linux distributions that are on copy on this may tell
> > > > you a different story. They all developed their own downstream patches to
> > > > somehow add the flexibility that is needed for them. So, we have a great
> > > > deal of fragmentation at the resting-foundation of Linux cryptography.
> > >
> > > What distros specifically have patches in their kernels that do
> > > different things to the random code path? Do you have pointers to those
> > > patches anywhere? Why have the distros not submitted their changes
> > > upstream?
> >
> > I will leave the representatives from the distros to chime in and point to
> > these patches.
>
> Then why not work with the distros to get these changes merged into the
> kernel tree? They know that keeping things out-of-the-tree costs them
> time and money, so why are they keeping them there?

I can speak for my distro.
We have not proposed them because they are hacks, we know they are
hacks, and we know they are not the long term solution.
Yet we have no better way (in our products, today) so far to deal with
these issues because what is needed is an effort like LRNG (does not
have to be this specific implementation), because hacks will not cut it
in the long term.

> I recommend getting the distros to chime in on what their requirements
> are for the random code would probably be best as they are the ones that
> take on the "random fips requirement of the day" more than anyone else.

Greg,
I think you can takes Stephan's introduction and supporting material
from this patchset to see what are the requirements. These patches have
not been maturing in a void, but Stephan basically distilled
discussions between multiple vendors as well as regulatory bodies (as
you can see he has reviews from BSI and NIST requirements are also
fully represented here).

He addressed a few aspects I can mention but are not the only ones:
performance (esp on NUMA systems), not blocking at boot due to lack of
entropy, NIST/BSI conformance, flexibility so that future regulatory
requirements can be easily integrated and upstreamed.


> > Yet, these changes are commonly a band-aid only that have some additional
> > drawbacks. Bottom line, there is no appropriate way with the current code to
> > allow vendors what they want to achieve. One hint to what changes vendors are
> > attempting can be found in [1] slide 20.
>
> What exactly do vendors "want to achieve"? Where are they saying this?
>
> > [1] https://www.chronox.de/lrng/doc/lrng_presentation_v43.pdf
>
> I see nothing on that slide that mentions actual requirements other than
> "the current code does not match this random government regulation".
>
> Please provide valid reasons, from distros.
>

Please let me know in what format you want to see these requirements,
and I will work with other to provide them.

Simo.


> thanks,
>
> greg k-h
>

--
Simo Sorce
RHEL Crypto Team
Red Hat, Inc





2021-11-22 17:08:07

by John Haxby

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator



> On 22 Nov 2021, at 06:02, Greg Kroah-Hartman <[email protected]> wrote:
>
> On Mon, Nov 22, 2021 at 06:34:43AM +0100, Stephan Mueller wrote:
>> Am Sonntag, 21. November 2021, 23:42:33 CET schrieb Jason A. Donenfeld:
>>
>> Hi Jason,
>>
>>> Hi Stephan,
>>>
>>> You've posted it again, and yet I still believe this is not the
>>> correct design or direction. I do not think the explicit goal of
>>> extended configurability ("flexibility") or the explicit goal of being
>>> FIPS compatible represent good directions, and I think this introduces
>>> new problems rather than solving any existing ones.
>>
>> The members from the Linux distributions that are on copy on this may tell you
>> a different story. They all developed their own downstream patches to somehow
>> add the flexibility that is needed for them. So, we have a great deal of
>> fragmentation at the resting-foundation of Linux cryptography.
>
> What distros specifically have patches in their kernels that do
> different things to the random code path? Do you have pointers to those
> patches anywhere? Why have the distros not submitted their changes
> upstream?
>

We (Oracle) are carrying such a patch at the moment. We want this patch to be a temporary workaround simply to get FIPS certification in the current kernel.

We're carrying this patch simply because the certification requirements changed and this was the quickest and easiest way to workaround today's problem. It won't fix tomorrow's problem and next time we, and others, attempt FIPS certification then we, and others, will need a different /dev/random because neither the old one nor our quick and dirty workaround will actually be acceptable.

The commit we're carrying at the moment is here: https://github.com/oracle/linux-uek/commit/5ebe83413c7573d566e581461bc95f9f139c5d4d -- you'll notice that we have a different RNG in fips mode compared to normal mode.

We really don't want to have to work out a new hack for future FIPS certifications and I'm sure no other distros want to do that either.

Personally, I'd far rather have any fips-certifiable /dev/random drivers be in mainline where everyone gets the same thing. I don't like carrying out-of-tree patches like this, it's not healthy.

jch


> thanks,
>
> greg k-h


Attachments:
signature.asc (228.00 B)
Message signed with OpenPGP

2021-11-22 21:07:16

by Jeffrey Walton

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Nov 22, 2021 at 10:10 AM Simo Sorce <[email protected]> wrote:
>
> On Mon, 2021-11-22 at 07:55 +0100, Greg Kroah-Hartman wrote:
> > On Mon, Nov 22, 2021 at 07:42:02AM +0100, Stephan Mueller wrote:
> > > ...
> > > I will leave the representatives from the distros to chime in and point to
> > > these patches.
> >
> > Then why not work with the distros to get these changes merged into the
> > kernel tree? They know that keeping things out-of-the-tree costs them
> > time and money, so why are they keeping them there?
>
> I can speak for my distro.
> We have not proposed them because they are hacks, we know they are
> hacks, and we know they are not the long term solution.
> Yet we have no better way (in our products, today) so far to deal with
> these issues because what is needed is an effort like LRNG (does not
> have to be this specific implementation), because hacks will not cut it
> in the long term.

Kernel support for FIPS validated crypto would be very useful, IMHO.

Currently most folks I know and consult with use CentOS because CentOS
is free and includes the FIPS canister for OpenSSL. Several folks I
know and consult with don't have a solution because they use Debian
derivatives, like Ubuntu. They use Ubuntu because Ubuntu offers the
image processing packages they need out of the box.

Moving the validated crypto into the kernel would be useful since all
distros can provide it without the need for one-off patches.

What I am less clear about.... NIST is only one standard body, and not
everyone trusts the US. There are other bodies that should probably be
represented, like KISA. So the big question becomes, how does the
kernel offer "approved" crypto for different consumers? (where
"approved" means blessed by some agency like NIST or KISA).

Jeff

2021-11-23 05:39:53

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Am Montag, 22. November 2021, 22:06:55 CET schrieb Jeffrey Walton:

Hi Jeffrey,

> On Mon, Nov 22, 2021 at 10:10 AM Simo Sorce <[email protected]> wrote:
> > On Mon, 2021-11-22 at 07:55 +0100, Greg Kroah-Hartman wrote:
> > > On Mon, Nov 22, 2021 at 07:42:02AM +0100, Stephan Mueller wrote:
> > > > ...
> > > > I will leave the representatives from the distros to chime in and
> > > > point to
> > > > these patches.
> > >
> > > Then why not work with the distros to get these changes merged into the
> > > kernel tree? They know that keeping things out-of-the-tree costs them
> > > time and money, so why are they keeping them there?
> >
> > I can speak for my distro.
> > We have not proposed them because they are hacks, we know they are
> > hacks, and we know they are not the long term solution.
> > Yet we have no better way (in our products, today) so far to deal with
> > these issues because what is needed is an effort like LRNG (does not
> > have to be this specific implementation), because hacks will not cut it
> > in the long term.
>
> Kernel support for FIPS validated crypto would be very useful, IMHO.
>
> Currently most folks I know and consult with use CentOS because CentOS
> is free and includes the FIPS canister for OpenSSL. Several folks I
> know and consult with don't have a solution because they use Debian
> derivatives, like Ubuntu. They use Ubuntu because Ubuntu offers the
> image processing packages they need out of the box.
>
> Moving the validated crypto into the kernel would be useful since all
> distros can provide it without the need for one-off patches.
>
> What I am less clear about.... NIST is only one standard body, and not
> everyone trusts the US. There are other bodies that should probably be
> represented, like KISA. So the big question becomes, how does the
> kernel offer "approved" crypto for different consumers? (where
> "approved" means blessed by some agency like NIST or KISA).

IMHO that is where the flexibility of the LRNG comes in. I am currently in
discussion with the German BSI on their requirements and these requirements
can be covered by a few extra lines since it only affects a different initial
seeding of the DRNG.

In any case, the LRNG supports other approaches by:

- select one or more entropy sources (or provide one from external) that are
considered appropriate

- if needed, adjust the initial seeding operation

- if needed, adjust the crypto primitives that are in use.

Ciao
Stephan
>
> Jeff


Ciao
Stephan



2021-11-25 05:27:41

by Chen, Rong A

[permalink] [raw]
Subject: Re: [kbuild-all] Re: [PATCH v43 01/15] Linux Random Number Generator



On 11/22/2021 7:47 PM, Stephan Mueller wrote:
> Am Montag, 22. November 2021, 11:33:26 CET schrieb kernel test robot:
>
> Hi,
>
>> All errors (new ones prefixed by >>):
>>>> drivers/char/lrng/lrng_chacha20.c:32:8: error: structure variable
>>>> 'chacha20' with 'latent_entropy' attribute has a non-integer field
>>>> 'block'
>> 32 | struct chacha20_state chacha20 __latent_entropy;
>>
>> | ^~~~~~~~~~~~~~
>>
>> vim +32 drivers/char/lrng/lrng_chacha20.c
>
> Thanks for the notification.
>
> I think this is a false-positive discussed before. __latent_entropy is
> seemingly allowed for an entire linear buffer as seen in the declaration of
> the variable input_pool_data in driver/char/random.c which is an array of u32.
>
> The struct chacha20_state is a linear buffer of u32 words.
>
> struct chacha20_block {
> u32 constants[4];
> union {
> u32 u[CHACHA_KEY_SIZE_WORDS];
> u8 b[CHACHA_KEY_SIZE];
> } key;
> u32 counter;
> u32 nonce[3];
> };
>
> Therefore it should be identical to the aforementioned example. The
> __latent_entropy marker therefore seems to be appropriate for this structure.
>
> Ciao
> Stephan
>
>

Hi Stephan,

Thanks for the explanation, we'll add the error to the ignore list.

Best Regards,
Rong Chen

2021-11-26 15:47:25

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Nov 22, 2021 at 04:56:24PM +0000, John Haxby wrote:
>
>
> > On 22 Nov 2021, at 06:02, Greg Kroah-Hartman <[email protected]> wrote:
> >
> > On Mon, Nov 22, 2021 at 06:34:43AM +0100, Stephan Mueller wrote:
> >> Am Sonntag, 21. November 2021, 23:42:33 CET schrieb Jason A. Donenfeld:
> >>
> >> Hi Jason,
> >>
> >>> Hi Stephan,
> >>>
> >>> You've posted it again, and yet I still believe this is not the
> >>> correct design or direction. I do not think the explicit goal of
> >>> extended configurability ("flexibility") or the explicit goal of being
> >>> FIPS compatible represent good directions, and I think this introduces
> >>> new problems rather than solving any existing ones.
> >>
> >> The members from the Linux distributions that are on copy on this may tell you
> >> a different story. They all developed their own downstream patches to somehow
> >> add the flexibility that is needed for them. So, we have a great deal of
> >> fragmentation at the resting-foundation of Linux cryptography.
> >
> > What distros specifically have patches in their kernels that do
> > different things to the random code path? Do you have pointers to those
> > patches anywhere? Why have the distros not submitted their changes
> > upstream?
> >
>
> We (Oracle) are carrying such a patch at the moment. We want this
> patch to be a temporary workaround simply to get FIPS certification in
> the current kernel.
>
> We're carrying this patch simply because the certification
> requirements changed and this was the quickest and easiest way to
> workaround today's problem. It won't fix tomorrow's problem and next
> time we, and others, attempt FIPS certification then we, and others,
> will need a different /dev/random because neither the old one nor our
> quick and dirty workaround will actually be acceptable.
>
> The commit we're carrying at the moment is here:
> https://github.com/oracle/linux-uek/commit/5ebe83413c7573d566e581461bc95f9f139c5d4d
> -- you'll notice that we have a different RNG in fips mode compared to
> normal mode.

Now that's a much smaller and simpler and easier to understand change,
compared to "rewrite the whole random number generator".

Why not work to get FIPS support merged properly upstream? I know there
are many people working to get FIPS certification, and while the
requirements seem to constantly change and are vague and random, it
would be great to stop duplicating all of this effort all the time.

> We really don't want to have to work out a new hack for future FIPS
> certifications and I'm sure no other distros want to do that either.

Great, why not work to solve this within what we have?

Remember, wholesale replacement is NOT how kernel development happens.
It's incremental evolution based on external need. It's not a "kill the
existing code and replace it from scratch" process.

> Personally, I'd far rather have any fips-certifiable /dev/random
> drivers be in mainline where everyone gets the same thing. I don't
> like carrying out-of-tree patches like this, it's not healthy.

Great, please work to make this happen within what we have today.

But adding a stand-alone separate random subsystem just for this is not
a good idea and is one huge reason why this patch set keeps being
ignored by the kernel developers.

thanks,

greg k-h

2021-11-26 15:48:39

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Nov 22, 2021 at 10:09:05AM -0500, Simo Sorce wrote:
> On Mon, 2021-11-22 at 07:55 +0100, Greg Kroah-Hartman wrote:
> > On Mon, Nov 22, 2021 at 07:42:02AM +0100, Stephan Mueller wrote:
> > > Am Montag, 22. November 2021, 07:02:14 CET schrieb Greg Kroah-Hartman:
> > >
> > > Hi Greg,
> > >
> > > > On Mon, Nov 22, 2021 at 06:34:43AM +0100, Stephan Mueller wrote:
> > > > > Am Sonntag, 21. November 2021, 23:42:33 CET schrieb Jason A. Donenfeld:
> > > > >
> > > > > Hi Jason,
> > > > >
> > > > > > Hi Stephan,
> > > > > >
> > > > > > You've posted it again, and yet I still believe this is not the
> > > > > > correct design or direction. I do not think the explicit goal of
> > > > > > extended configurability ("flexibility") or the explicit goal of being
> > > > > > FIPS compatible represent good directions, and I think this introduces
> > > > > > new problems rather than solving any existing ones.
> > > > >
> > > > > The members from the Linux distributions that are on copy on this may tell
> > > > > you a different story. They all developed their own downstream patches to
> > > > > somehow add the flexibility that is needed for them. So, we have a great
> > > > > deal of fragmentation at the resting-foundation of Linux cryptography.
> > > >
> > > > What distros specifically have patches in their kernels that do
> > > > different things to the random code path? Do you have pointers to those
> > > > patches anywhere? Why have the distros not submitted their changes
> > > > upstream?
> > >
> > > I will leave the representatives from the distros to chime in and point to
> > > these patches.
> >
> > Then why not work with the distros to get these changes merged into the
> > kernel tree? They know that keeping things out-of-the-tree costs them
> > time and money, so why are they keeping them there?
>
> I can speak for my distro.
> We have not proposed them because they are hacks, we know they are
> hacks, and we know they are not the long term solution.

Hacks that work today are the step toward a real solution.

So please, propose them and we can evolve from that.

> Yet we have no better way (in our products, today) so far to deal with
> these issues because what is needed is an effort like LRNG (does not
> have to be this specific implementation), because hacks will not cut it
> in the long term.

So you want to ship this separate driver instead? Great, but as I said
elsewhere, doing a wholesale replacement is almost never the right thing
to do.

Work off of those known-working-and-certified hacks. Submit them and
let's go from there please.

thanks,

greg k-h

2021-11-26 15:55:37

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Nov 22, 2021 at 09:59:01AM -0500, Simo Sorce wrote:
> Jason,
> have you previously produced a list of reasoned concerns with this
> patchset and direction?
>
> This specific email is not really useful to me to understand the
> concerns as it does not contain actionable suggestion or critique.
>
> I personally find the direction fine, and with my distribution hat on I
> can say that FIPS is essential for us and any design must include an
> option to be FIPS certifiable.
>
> As NIST keeps improving their testing capabilities and rigorous
> cryptographic design of the CSPRNGs as well as entropy sources the
> kernel must also adapt.
>
> Stephan is providing a path forward, and I haven't seen any other
> proposal, let alone code, that provide improvements in this area.
> I am pretty sure the design can be improved if there is detailed and
> actionable feedback on what to change.
>
> I hope the path forward can be one of collaboration rather then mere
> opposition.

Replacement of the existing code to cut over to the new one is not
collaboration, it's the exact opposite.

Submitting patches to the existing codebase to implement the
"requirements" is the proper way forward, why has that never been done.

Remember, evolution is the correct way of kernel development, not
intelligent design :)

thanks,

greg k-h

2021-11-26 16:19:41

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Am Freitag, 26. November 2021, 16:44:17 CET schrieb Greg Kroah-Hartman:

Hi Greg,

> On Mon, Nov 22, 2021 at 09:59:01AM -0500, Simo Sorce wrote:
> > Jason,
> > have you previously produced a list of reasoned concerns with this
> > patchset and direction?
> >
> > This specific email is not really useful to me to understand the
> > concerns as it does not contain actionable suggestion or critique.
> >
> > I personally find the direction fine, and with my distribution hat on I
> > can say that FIPS is essential for us and any design must include an
> > option to be FIPS certifiable.
> >
> > As NIST keeps improving their testing capabilities and rigorous
> > cryptographic design of the CSPRNGs as well as entropy sources the
> > kernel must also adapt.
> >
> > Stephan is providing a path forward, and I haven't seen any other
> > proposal, let alone code, that provide improvements in this area.
> > I am pretty sure the design can be improved if there is detailed and
> > actionable feedback on what to change.
> >
> > I hope the path forward can be one of collaboration rather then mere
> > opposition.
>
> Replacement of the existing code to cut over to the new one is not
> collaboration, it's the exact opposite.
>
> Submitting patches to the existing codebase to implement the
> "requirements" is the proper way forward, why has that never been done.

It has been attempted by Nikolai Stange without avail - no comments were
received, let alone it was integrated.
>
> Remember, evolution is the correct way of kernel development, not
> intelligent design :)
>
> thanks,
>
> greg k-h


Ciao
Stephan



2021-11-26 16:38:31

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Fri, Nov 26, 2021 at 05:15:59PM +0100, Stephan Mueller wrote:
> Am Freitag, 26. November 2021, 16:44:17 CET schrieb Greg Kroah-Hartman:
>
> Hi Greg,
>
> > On Mon, Nov 22, 2021 at 09:59:01AM -0500, Simo Sorce wrote:
> > > Jason,
> > > have you previously produced a list of reasoned concerns with this
> > > patchset and direction?
> > >
> > > This specific email is not really useful to me to understand the
> > > concerns as it does not contain actionable suggestion or critique.
> > >
> > > I personally find the direction fine, and with my distribution hat on I
> > > can say that FIPS is essential for us and any design must include an
> > > option to be FIPS certifiable.
> > >
> > > As NIST keeps improving their testing capabilities and rigorous
> > > cryptographic design of the CSPRNGs as well as entropy sources the
> > > kernel must also adapt.
> > >
> > > Stephan is providing a path forward, and I haven't seen any other
> > > proposal, let alone code, that provide improvements in this area.
> > > I am pretty sure the design can be improved if there is detailed and
> > > actionable feedback on what to change.
> > >
> > > I hope the path forward can be one of collaboration rather then mere
> > > opposition.
> >
> > Replacement of the existing code to cut over to the new one is not
> > collaboration, it's the exact opposite.
> >
> > Submitting patches to the existing codebase to implement the
> > "requirements" is the proper way forward, why has that never been done.
>
> It has been attempted by Nikolai Stange without avail - no comments were
> received, let alone it was integrated.

Links to the patches and discussion please?

thanks,

greg k-h

2021-11-29 15:35:54

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Am Freitag, 26. November 2021, 17:22:14 CET schrieb Greg Kroah-Hartman:

Hi Greg,

> On Fri, Nov 26, 2021 at 05:15:59PM +0100, Stephan Mueller wrote:
> > Am Freitag, 26. November 2021, 16:44:17 CET schrieb Greg Kroah-Hartman:
> >
> > Hi Greg,
> >
> > > On Mon, Nov 22, 2021 at 09:59:01AM -0500, Simo Sorce wrote:
> > > > Jason,
> > > > have you previously produced a list of reasoned concerns with this
> > > > patchset and direction?
> > > >
> > > > This specific email is not really useful to me to understand the
> > > > concerns as it does not contain actionable suggestion or critique.
> > > >
> > > > I personally find the direction fine, and with my distribution hat on
> > > > I
> > > > can say that FIPS is essential for us and any design must include an
> > > > option to be FIPS certifiable.
> > > >
> > > > As NIST keeps improving their testing capabilities and rigorous
> > > > cryptographic design of the CSPRNGs as well as entropy sources the
> > > > kernel must also adapt.
> > > >
> > > > Stephan is providing a path forward, and I haven't seen any other
> > > > proposal, let alone code, that provide improvements in this area.
> > > > I am pretty sure the design can be improved if there is detailed and
> > > > actionable feedback on what to change.
> > > >
> > > > I hope the path forward can be one of collaboration rather then mere
> > > > opposition.
> > >
> > > Replacement of the existing code to cut over to the new one is not
> > > collaboration, it's the exact opposite.
> > >
> > > Submitting patches to the existing codebase to implement the
> > > "requirements" is the proper way forward, why has that never been done.
> >
> > It has been attempted by Nikolai Stange without avail - no comments were
> > received, let alone it was integrated.
>
> Links to the patches and discussion please?

Please consider https://lkml.org/lkml/2020/9/21/157

One side note: the LRNG patch set does not replace random.c, but provides an
additional implementation that can be selected at compile time. I am under the
impression that is an equal approach considering other areas of the kernel
like file systems, memory allocators, and similar.

Thanks
Stephan



2021-11-29 16:54:25

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Am Montag, 29. November 2021, 17:25:24 CET schrieb Greg Kroah-Hartman:

Hi Greg,

> > >
> > > Links to the patches and discussion please?
> >
> > Please consider https://lkml.org/lkml/2020/9/21/157
>
> That's a load of patches, some of them seem sane, what ever happened to
> them?

Nothing was discussed, nothing was picked up.

> Seems like the conversation got derailed by people with email
> server issues that prevented them from participating in public :(
>
> But that patch set is a nice way to do this, incremental changes working
> with the existing codebase, not trying to ignore the current code and
> create a separate implementation.
>
> Also, minor note, please use lore.kernel.org links, we don't have any
> control over lkml.org, nor can we take patches out of that site with any
> of our normal tools.

Apologies, will do for the next time.


Ciao
Stephan



2021-11-29 19:55:55

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Nov 29, 2021 at 04:31:59PM +0100, Stephan Mueller wrote:
> Am Freitag, 26. November 2021, 17:22:14 CET schrieb Greg Kroah-Hartman:
>
> Hi Greg,
>
> > On Fri, Nov 26, 2021 at 05:15:59PM +0100, Stephan Mueller wrote:
> > > Am Freitag, 26. November 2021, 16:44:17 CET schrieb Greg Kroah-Hartman:
> > >
> > > Hi Greg,
> > >
> > > > On Mon, Nov 22, 2021 at 09:59:01AM -0500, Simo Sorce wrote:
> > > > > Jason,
> > > > > have you previously produced a list of reasoned concerns with this
> > > > > patchset and direction?
> > > > >
> > > > > This specific email is not really useful to me to understand the
> > > > > concerns as it does not contain actionable suggestion or critique.
> > > > >
> > > > > I personally find the direction fine, and with my distribution hat on
> > > > > I
> > > > > can say that FIPS is essential for us and any design must include an
> > > > > option to be FIPS certifiable.
> > > > >
> > > > > As NIST keeps improving their testing capabilities and rigorous
> > > > > cryptographic design of the CSPRNGs as well as entropy sources the
> > > > > kernel must also adapt.
> > > > >
> > > > > Stephan is providing a path forward, and I haven't seen any other
> > > > > proposal, let alone code, that provide improvements in this area.
> > > > > I am pretty sure the design can be improved if there is detailed and
> > > > > actionable feedback on what to change.
> > > > >
> > > > > I hope the path forward can be one of collaboration rather then mere
> > > > > opposition.
> > > >
> > > > Replacement of the existing code to cut over to the new one is not
> > > > collaboration, it's the exact opposite.
> > > >
> > > > Submitting patches to the existing codebase to implement the
> > > > "requirements" is the proper way forward, why has that never been done.
> > >
> > > It has been attempted by Nikolai Stange without avail - no comments were
> > > received, let alone it was integrated.
> >
> > Links to the patches and discussion please?
>
> Please consider https://lkml.org/lkml/2020/9/21/157

That's a load of patches, some of them seem sane, what ever happened to
them? Seems like the conversation got derailed by people with email
server issues that prevented them from participating in public :(

But that patch set is a nice way to do this, incremental changes working
with the existing codebase, not trying to ignore the current code and
create a separate implementation.

Also, minor note, please use lore.kernel.org links, we don't have any
control over lkml.org, nor can we take patches out of that site with any
of our normal tools.

> One side note: the LRNG patch set does not replace random.c, but provides an
> additional implementation that can be selected at compile time. I am under the
> impression that is an equal approach considering other areas of the kernel
> like file systems, memory allocators, and similar.

Sometimes, yes, it is valid to have different implementations for things
that do different things in the same area (like filesystems), but for a
core function of the kernel, so far the existing random maintainer has
not wanted to have multiple implementations. Same goes for other parts
of the kernel, it's not specific only to this one very tiny driver.

As a counterpoint, we do not allow duplicate drivers that control the
same hardware types in the tree. We have tried that in the past and it
was a nightmare to support and maintain and just caused massive user
confusion as well. One can argue that the random driver is in this same
category.

thanks,

greg k-h

2021-11-30 02:55:30

by Sandy Harris

[permalink] [raw]
Subject: Re: [kbuild-all] Re: [PATCH v43 01/15] Linux Random Number Generator

Chen, Rong A <[email protected]> wrote:

> On 11/22/2021 7:47 PM, Stephan Mueller wrote:

> > Thanks for the notification.
> >
> > I think this is a false-positive discussed before. __latent_entropy is
> > seemingly allowed for an entire linear buffer as seen in the declaration of
> > the variable input_pool_data in driver/char/random.c which is an array of u32.
> >
> > The struct chacha20_state is a linear buffer of u32 words.
> >
> > struct chacha20_block {
> > u32 constants[4];
> > union {
> > u32 u[CHACHA_KEY_SIZE_WORDS];
> > u8 b[CHACHA_KEY_SIZE];
> > } key;
> > u32 counter;
> > u32 nonce[3];
> > };
> >
> > Therefore it should be identical to the aforementioned example.

No. It is a struct & there's no guarantee all compilers will lay
it out as you expect. There might even be a gap in the layout
since nonce[] has an odd number of elements.

>> The __latent_entropy marker therefore seems to be appropriate for this structure.

First, this is completely unnecessary since the input pool is marked for
latent entropy & changes there will affect the chacha context.

Also, if I'm reading the docs right, the __latent_entropy attribute
on a data structure only gets it initialised somewhat randomly.
If you want a continuous effect at runtime, then you need to
make the code mix the latent_entropy global variable into the
data structure.

2021-11-30 06:07:01

by Stephan Müller

[permalink] [raw]
Subject: Re: [kbuild-all] Re: [PATCH v43 01/15] Linux Random Number Generator

Am Dienstag, 30. November 2021, 03:55:12 CET schrieb Sandy Harris:

Hi Sandy,

> Chen, Rong A <[email protected]> wrote:
> > On 11/22/2021 7:47 PM, Stephan Mueller wrote:
> > > Thanks for the notification.
> > >
> > > I think this is a false-positive discussed before. __latent_entropy is
> > > seemingly allowed for an entire linear buffer as seen in the declaration
> > > of
> > > the variable input_pool_data in driver/char/random.c which is an array
> > > of u32.
> > >
> > > The struct chacha20_state is a linear buffer of u32 words.
> > >
> > > struct chacha20_block {
> > >
> > > u32 constants[4];
> > > union {
> > >
> > > u32 u[CHACHA_KEY_SIZE_WORDS];
> > > u8 b[CHACHA_KEY_SIZE];
> > >
> > > } key;
> > > u32 counter;
> > > u32 nonce[3];
> > >
> > > };
> > >
> > > Therefore it should be identical to the aforementioned example.
>
> No. It is a struct & there's no guarantee all compilers will lay
> it out as you expect. There might even be a gap in the layout
> since nonce[] has an odd number of elements.
>
> >> The __latent_entropy marker therefore seems to be appropriate for this
> >> structure.
> First, this is completely unnecessary since the input pool is marked for
> latent entropy & changes there will affect the chacha context.
>
> Also, if I'm reading the docs right, the __latent_entropy attribute
> on a data structure only gets it initialised somewhat randomly.
> If you want a continuous effect at runtime, then you need to
> make the code mix the latent_entropy global variable into the
> data structure.

Thank you very much for your explanation. I will change my code accordingly.

Note, the LRNG does not have an input_pool.

Ciao
Stephan



2021-11-30 07:32:54

by Sandy Harris

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Nov 22, 2021 at 11:02 PM Simo Sorce <[email protected]> wrote:

> ... with my distribution hat on I
> can say that FIPS is essential for us and any design must include an
> option to be FIPS certifiable.

I think I understand Ted's objections & I'm inclined to agree with
them. See also this comment from Jon Callas:

" The NIST AES_CTR_DRBG is mostly-okay. It's got a few issues
" (see <https://eprint.iacr.org/2020/619.pdf>), but you can get
" around them. I don't like that it takes a simple concept (a good block
" cipher is a good PRP/PRF) and throws enough scaffolding around it
" to make it hard to understand. I understand the reasons .., it just
" bugs me.

That said, people do want compliance with FIPS or various other
standards. That raises a number of questions.

One is for Stephan: would you care to submit patches for the
current driver that ONLY make it FIPS (and/or European standards)
compliant? No jitter, no stuff to allow different crypto, no choice to
replace the driver, ..., just FIPS. Some of those might be good
ideas, but they would not belong in a FIPS patch.

I think that would have a much better chance of acceptance
than your patches to date.

There are also more general questions:

The FIPS requirements for a deterministic RNG include a
whole rat's nest of extras, notably various self-tests, which
Stephan's patches might deal with. Or do any of the vendors
have patches for that?

FIPS defines DRNGs using either a block cipher or a hash,
but we use a stream cipher. According to Wikipedia, several
of the *BSD distros do the same.
https://en.wikipedia.org/wiki/Salsa20#ChaCha20_adoption
This seems reasonable to me, but could we persuade NIST
to add that option? I've added John Kelsey (one of the FIPS
authors) to the cc list in hopes of that.

Failing that, the Blake hash function is based on Chacha
https://en.wikipedia.org/wiki/BLAKE_(hash_function)
Should we use that to get a more easily certified DRNG?
Would that be as fast as Chacha alone?

The FIPS also require that all the entropy inputs to the
DRNG be certified. I think we have a problem there.

I suspect that many of the random number instructions
enabled by CONFIG_ARCH_RANDOM or the hardware
RNGs enabled by CONFIG_HW_RANDOM could be
certified, but that would need to be done by the vendors
and the costs might be substantial. Are any already
certified? Is there any vendor interest?

Apart from those, the current driver gets entropy in
several places. The comments have:

* The current exported interfaces for gathering environmental noise
* from the devices are:
*
* void add_device_randomness(const void *buf, unsigned int size);
* void add_input_randomness(unsigned int type, unsigned int code,
* unsigned int value);
* void add_interrupt_randomness(int irq, int irq_flags);
* void add_disk_randomness(struct gendisk *disk);
*
* add_device_randomness() is for adding data to the random pool that
* is likely to differ between two devices (or possibly even per boot).
* This would be things like MAC addresses or serial numbers, ...
*
* add_input_randomness() uses the input layer interrupt timing, as well as
* the event type information from the hardware.
*
* add_interrupt_randomness() uses the interrupt timing as random
* inputs to the entropy pool. Using the cycle counters and the irq source
* as inputs, it feeds the randomness roughly once a second.
*
* add_disk_randomness() uses what amounts to the seek time of block
* layer request events, ... Note that high-speed solid state drives with
* very low seek times do not make for good sources of entropy, ...

I think we should eliminate add_disk_randomness() since it does
not work well on current hardware. Also, FIPS requires that
entropy sources be independent & add_interrupt_randomness()
depends on the same disk events so these sources may not be.

A similar argument could be made for getting rid of
add_input_randomness() but that would lose the event
type information. Does that matter?

The current driver also uses other sources, at least
fast_mix(), add_timer_randomness(), random_get_entropy()
and the gcc latent entropy plugin. Could/should those be
simplified to get more easily audited or certified code?

The driver also allows input of external data which is
mixed into the pool & provides an ioctl to let a user
with root privileges change the pool's entropy estimate.
Do either of those violate the FIPS requirement that
only certified entropy sources be used? I'd be happy
to lose the ioctl (or better, all the entropy estimation
machinery) but those have been part of the system
for decades. I'd definitely want to keep the option to
use external inputs.

2021-11-30 08:47:05

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Tue, Nov 30, 2021 at 03:32:38PM +0800, Sandy Harris wrote:
> I think we should eliminate add_disk_randomness() since it does
> not work well on current hardware. Also, FIPS requires that
> entropy sources be independent & add_interrupt_randomness()
> depends on the same disk events so these sources may not be.

This whole "may not be" guessing game when it comes to FIPS
certification is a huge problem. I have heard of different vendors
getting different feedback and different implementations "passing" in
different ways that totally contradict each other. It seems that there
is a whole certification industry built up that you can use to try to
pass these tests, but those tests are different depending on the vendor
you use for this, making a total mess.

So perhaps getting solid answers, and having the FIPS people actually
implement (or at least review) the changes and submit them (this is all
open for everyone to see and work on), would be the best thing as that
would at least let us know that this is what they require.

Otherwise, it's a total guess as you state many times in this email, and
that is going to get us nowhere fast as the "requirements" end up
contradicting themselves all the time.

Also, why does any of this have to be in the kernel at all? If FIPS
requires a deterministic random number generator that will not allow
entropy to be acquired from hardware or external inputs, why does the
kernel care at all? Just write a fips_random.so library and get it
certified and have any userspace code that cares about such a crazy
thing to use that instead.

thanks,

greg k-h

2021-11-30 08:58:11

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Am Dienstag, 30. November 2021, 08:55:53 CET schrieb Greg Kroah-Hartman:

Hi Greg,

> On Tue, Nov 30, 2021 at 03:32:38PM +0800, Sandy Harris wrote:
> > I think we should eliminate add_disk_randomness() since it does
> > not work well on current hardware. Also, FIPS requires that
> > entropy sources be independent & add_interrupt_randomness()
> > depends on the same disk events so these sources may not be.
>
> This whole "may not be" guessing game when it comes to FIPS
> certification is a huge problem. I have heard of different vendors
> getting different feedback and different implementations "passing" in
> different ways that totally contradict each other. It seems that there
> is a whole certification industry built up that you can use to try to
> pass these tests, but those tests are different depending on the vendor
> you use for this, making a total mess.
>
> So perhaps getting solid answers, and having the FIPS people actually
> implement (or at least review) the changes and submit them (this is all
> open for everyone to see and work on), would be the best thing as that
> would at least let us know that this is what they require.

Just as a note: I am working as FIPS tester. I am part of the NIST entropy
working group which oversees the entropy related requirements. The LRNG's FIPS
compliant implementation is directly based on those requirements. The LRNG was
even reviewed by NIST personnel who mentioned that they do not see any
contradiction to the specification. Finally, we are pursuing to get a separate
ENT validation from NIST for the LRNG which would indicate that the LRNG meets
all their requirements.

Besides, the LRNG can be configured to have no FIPS bits included at all as
documented in the patches and in the separately provided documentation. Yet,
it offers a streamlined conditioning operation and a combination of different
entropy source data which is obvious to not destroy entropy.

Ciao
Stephan



2021-11-30 09:12:54

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Tue, Nov 30, 2021 at 09:56:41AM +0100, Stephan Mueller wrote:
> Am Dienstag, 30. November 2021, 08:55:53 CET schrieb Greg Kroah-Hartman:
>
> Hi Greg,
>
> > On Tue, Nov 30, 2021 at 03:32:38PM +0800, Sandy Harris wrote:
> > > I think we should eliminate add_disk_randomness() since it does
> > > not work well on current hardware. Also, FIPS requires that
> > > entropy sources be independent & add_interrupt_randomness()
> > > depends on the same disk events so these sources may not be.
> >
> > This whole "may not be" guessing game when it comes to FIPS
> > certification is a huge problem. I have heard of different vendors
> > getting different feedback and different implementations "passing" in
> > different ways that totally contradict each other. It seems that there
> > is a whole certification industry built up that you can use to try to
> > pass these tests, but those tests are different depending on the vendor
> > you use for this, making a total mess.
> >
> > So perhaps getting solid answers, and having the FIPS people actually
> > implement (or at least review) the changes and submit them (this is all
> > open for everyone to see and work on), would be the best thing as that
> > would at least let us know that this is what they require.
>
> Just as a note: I am working as FIPS tester. I am part of the NIST entropy
> working group which oversees the entropy related requirements. The LRNG's FIPS
> compliant implementation is directly based on those requirements. The LRNG was
> even reviewed by NIST personnel who mentioned that they do not see any
> contradiction to the specification. Finally, we are pursuing to get a separate
> ENT validation from NIST for the LRNG which would indicate that the LRNG meets
> all their requirements.

That's great, so you would be one of the best people to help submit
changes to the existing code to have it be compliant, instead of
replacing it entirely :)

thanks,

greg k-h

2021-11-30 12:24:30

by Jeffrey Walton

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Nov 29, 2021 at 6:07 PM Greg Kroah-Hartman
<[email protected]> wrote:
> ...
> Sometimes, yes, it is valid to have different implementations for things
> that do different things in the same area (like filesystems), but for a
> core function of the kernel, so far the existing random maintainer has
> not wanted to have multiple implementations. Same goes for other parts
> of the kernel, it's not specific only to this one very tiny driver.
>
> As a counterpoint, we do not allow duplicate drivers that control the
> same hardware types in the tree. We have tried that in the past and it
> was a nightmare to support and maintain and just caused massive user
> confusion as well. One can argue that the random driver is in this same
> category.

I think an argument could be made that they are different drivers
since they have different requirements and security goals. I don't
think it matters where the requirements came from, whether it was ad
hoc from the developer, NIST, KISA, CRYPTREC, NESSIE, or another
organization.

Maybe the problem is with the name of the driver? Perhaps the current
driver should be named random-linux, Stephan's driver should be named
random-nist, and the driver should be wired up based on a user's
selection. That should sidestep the problems associated with the
"duplicate drivers" policy.

Jeff

2021-11-30 14:04:28

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Tue, Nov 30, 2021 at 07:24:15AM -0500, Jeffrey Walton wrote:
> On Mon, Nov 29, 2021 at 6:07 PM Greg Kroah-Hartman
> <[email protected]> wrote:
> > ...
> > Sometimes, yes, it is valid to have different implementations for things
> > that do different things in the same area (like filesystems), but for a
> > core function of the kernel, so far the existing random maintainer has
> > not wanted to have multiple implementations. Same goes for other parts
> > of the kernel, it's not specific only to this one very tiny driver.
> >
> > As a counterpoint, we do not allow duplicate drivers that control the
> > same hardware types in the tree. We have tried that in the past and it
> > was a nightmare to support and maintain and just caused massive user
> > confusion as well. One can argue that the random driver is in this same
> > category.
>
> I think an argument could be made that they are different drivers
> since they have different requirements and security goals. I don't
> think it matters where the requirements came from, whether it was ad
> hoc from the developer, NIST, KISA, CRYPTREC, NESSIE, or another
> organization.
>
> Maybe the problem is with the name of the driver? Perhaps the current
> driver should be named random-linux, Stephan's driver should be named
> random-nist, and the driver should be wired up based on a user's
> selection. That should sidestep the problems associated with the
> "duplicate drivers" policy.

The "problem" here is that the drivers/char/random.c file has three users,
the userspace /dev/random and syscall api, the in-kernel "here's some
entropy for the random core to use" api, and the in-kernel "give me some
random data" api.

Odds are, you REALLY do not want the in-kernel calls to be pulling from
the "random-government-crippled-specification" implementation, right?

Again, just try evolving the existing code to meet the needs that you
all have, stop trying to do wholesale reimplementations. Those never
succeed, and it's pretty obvious that no one wants a "plugin a random
random driver" interface, right?

thanks,

greg k-h

2021-11-30 14:31:17

by Simo Sorce

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Tue, 2021-11-30 at 15:04 +0100, Greg Kroah-Hartman wrote:
> On Tue, Nov 30, 2021 at 07:24:15AM -0500, Jeffrey Walton wrote:
> > On Mon, Nov 29, 2021 at 6:07 PM Greg Kroah-Hartman
> > <[email protected]> wrote:
> > > ...
> > > Sometimes, yes, it is valid to have different implementations for things
> > > that do different things in the same area (like filesystems), but for a
> > > core function of the kernel, so far the existing random maintainer has
> > > not wanted to have multiple implementations. Same goes for other parts
> > > of the kernel, it's not specific only to this one very tiny driver.
> > >
> > > As a counterpoint, we do not allow duplicate drivers that control the
> > > same hardware types in the tree. We have tried that in the past and it
> > > was a nightmare to support and maintain and just caused massive user
> > > confusion as well. One can argue that the random driver is in this same
> > > category.
> >
> > I think an argument could be made that they are different drivers
> > since they have different requirements and security goals. I don't
> > think it matters where the requirements came from, whether it was ad
> > hoc from the developer, NIST, KISA, CRYPTREC, NESSIE, or another
> > organization.
> >
> > Maybe the problem is with the name of the driver? Perhaps the current
> > driver should be named random-linux, Stephan's driver should be named
> > random-nist, and the driver should be wired up based on a user's
> > selection. That should sidestep the problems associated with the
> > "duplicate drivers" policy.
>
> The "problem" here is that the drivers/char/random.c file has three users,
> the userspace /dev/random and syscall api, the in-kernel "here's some
> entropy for the random core to use" api, and the in-kernel "give me some
> random data" api.
>
> Odds are, you REALLY do not want the in-kernel calls to be pulling from
> the "random-government-crippled-specification" implementation, right?

You really *do* want that.
When our customers are mandated to use FIPS certified cryptography,
they want to use it for kernel cryptography as well, and in general
they want to use a certified randomness source as well.

I do not get why you call the implementation crippled? The
specification is quite thorough and provides well reasoned requirements
as well as self-test that insure coding mistakes won't end up returning
non-random values.

I understand the mistrust vs gov agencies due to past mishaps like the
Dual-DRBG thing, but we are not talking about something like that in
this case. NIST is not mandating any specific algorithmic
implementation, the requirement set forth allow to use a variety of
different algorithms so that everyone can choose what they think is
sane.

> Again, just try evolving the existing code to meet the needs that you
> all have, stop trying to do wholesale reimplementations. Those never
> succeed, and it's pretty obvious that no one wants a "plugin a random
> random driver" interface, right?

I think one of the issues is that the number of changes required
against the current random driver amount essentially to a re-
implementation. Sure, you can do it as a series of patches that
transform the current code in something completely different.

And the main question here is, how can we get there, in any case, if
the maintainer of the random device doesn't even participate in
discussions, does not pick obvious bug fixes and is simply not engaging
at all?

Your plan requires an active maintainer that guides these changes and
interact with the people proposing them to negotiate the best outcome.
But that is not happening so that road seem blocked at the moment.

HTH,
Simo.

--
Simo Sorce
RHEL Crypto Team
Red Hat, Inc





2021-11-30 15:22:39

by Jeffrey Walton

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Tue, Nov 30, 2021 at 9:04 AM Greg Kroah-Hartman
<[email protected]> wrote:
>
> On Tue, Nov 30, 2021 at 07:24:15AM -0500, Jeffrey Walton wrote:
> > On Mon, Nov 29, 2021 at 6:07 PM Greg Kroah-Hartman
> > <[email protected]> wrote:
> > > ...
> > > Sometimes, yes, it is valid to have different implementations for things
> > > that do different things in the same area (like filesystems), but for a
> > > core function of the kernel, so far the existing random maintainer has
> > > not wanted to have multiple implementations. Same goes for other parts
> > > of the kernel, it's not specific only to this one very tiny driver.
> > >
> > > As a counterpoint, we do not allow duplicate drivers that control the
> > > same hardware types in the tree. We have tried that in the past and it
> > > was a nightmare to support and maintain and just caused massive user
> > > confusion as well. One can argue that the random driver is in this same
> > > category.
> >
> > I think an argument could be made that they are different drivers
> > since they have different requirements and security goals. I don't
> > think it matters where the requirements came from, whether it was ad
> > hoc from the developer, NIST, KISA, CRYPTREC, NESSIE, or another
> > organization.
> >
> > Maybe the problem is with the name of the driver? Perhaps the current
> > driver should be named random-linux, Stephan's driver should be named
> > random-nist, and the driver should be wired up based on a user's
> > selection. That should sidestep the problems associated with the
> > "duplicate drivers" policy.
>
> The "problem" here is that the drivers/char/random.c file has three users,
> the userspace /dev/random and syscall api, the in-kernel "here's some
> entropy for the random core to use" api, and the in-kernel "give me some
> random data" api.
>
> Odds are, you REALLY do not want the in-kernel calls to be pulling from
> the "random-government-crippled-specification" implementation, right?

It's not a question of whether some folks want it or not. They have to
accept it due to policy. They have no choice in the matter.

I hope I don't sound argumentative. It's not my intention. But I know
what it's like to have to comply with policies, even ones I don't
like.

Jeff

2021-11-30 15:39:22

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Tue, Nov 30, 2021 at 10:13:26AM -0500, Jeffrey Walton wrote:
> On Tue, Nov 30, 2021 at 9:04 AM Greg Kroah-Hartman
> <[email protected]> wrote:
> >
> > On Tue, Nov 30, 2021 at 07:24:15AM -0500, Jeffrey Walton wrote:
> > > On Mon, Nov 29, 2021 at 6:07 PM Greg Kroah-Hartman
> > > <[email protected]> wrote:
> > > > ...
> > > > Sometimes, yes, it is valid to have different implementations for things
> > > > that do different things in the same area (like filesystems), but for a
> > > > core function of the kernel, so far the existing random maintainer has
> > > > not wanted to have multiple implementations. Same goes for other parts
> > > > of the kernel, it's not specific only to this one very tiny driver.
> > > >
> > > > As a counterpoint, we do not allow duplicate drivers that control the
> > > > same hardware types in the tree. We have tried that in the past and it
> > > > was a nightmare to support and maintain and just caused massive user
> > > > confusion as well. One can argue that the random driver is in this same
> > > > category.
> > >
> > > I think an argument could be made that they are different drivers
> > > since they have different requirements and security goals. I don't
> > > think it matters where the requirements came from, whether it was ad
> > > hoc from the developer, NIST, KISA, CRYPTREC, NESSIE, or another
> > > organization.
> > >
> > > Maybe the problem is with the name of the driver? Perhaps the current
> > > driver should be named random-linux, Stephan's driver should be named
> > > random-nist, and the driver should be wired up based on a user's
> > > selection. That should sidestep the problems associated with the
> > > "duplicate drivers" policy.
> >
> > The "problem" here is that the drivers/char/random.c file has three users,
> > the userspace /dev/random and syscall api, the in-kernel "here's some
> > entropy for the random core to use" api, and the in-kernel "give me some
> > random data" api.
> >
> > Odds are, you REALLY do not want the in-kernel calls to be pulling from
> > the "random-government-crippled-specification" implementation, right?
>
> It's not a question of whether some folks want it or not. They have to
> accept it due to policy. They have no choice in the matter.

I strongly doubt that policy dictates all of the current calls to
get_random_*() require that they return data that is dictated by that
policy. If so, that's not a valid specification for a variety of
reasons (i.e. it will break other specification requirements...)

thanks,

greg k-h

2021-11-30 15:46:00

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Tue, Nov 30, 2021 at 09:31:09AM -0500, Simo Sorce wrote:
> On Tue, 2021-11-30 at 15:04 +0100, Greg Kroah-Hartman wrote:
> > On Tue, Nov 30, 2021 at 07:24:15AM -0500, Jeffrey Walton wrote:
> > > On Mon, Nov 29, 2021 at 6:07 PM Greg Kroah-Hartman
> > > <[email protected]> wrote:
> > > > ...
> > > > Sometimes, yes, it is valid to have different implementations for things
> > > > that do different things in the same area (like filesystems), but for a
> > > > core function of the kernel, so far the existing random maintainer has
> > > > not wanted to have multiple implementations. Same goes for other parts
> > > > of the kernel, it's not specific only to this one very tiny driver.
> > > >
> > > > As a counterpoint, we do not allow duplicate drivers that control the
> > > > same hardware types in the tree. We have tried that in the past and it
> > > > was a nightmare to support and maintain and just caused massive user
> > > > confusion as well. One can argue that the random driver is in this same
> > > > category.
> > >
> > > I think an argument could be made that they are different drivers
> > > since they have different requirements and security goals. I don't
> > > think it matters where the requirements came from, whether it was ad
> > > hoc from the developer, NIST, KISA, CRYPTREC, NESSIE, or another
> > > organization.
> > >
> > > Maybe the problem is with the name of the driver? Perhaps the current
> > > driver should be named random-linux, Stephan's driver should be named
> > > random-nist, and the driver should be wired up based on a user's
> > > selection. That should sidestep the problems associated with the
> > > "duplicate drivers" policy.
> >
> > The "problem" here is that the drivers/char/random.c file has three users,
> > the userspace /dev/random and syscall api, the in-kernel "here's some
> > entropy for the random core to use" api, and the in-kernel "give me some
> > random data" api.
> >
> > Odds are, you REALLY do not want the in-kernel calls to be pulling from
> > the "random-government-crippled-specification" implementation, right?
>
> You really *do* want that.
> When our customers are mandated to use FIPS certified cryptography,
> they want to use it for kernel cryptography as well, and in general
> they want to use a certified randomness source as well.

There are huge numbers of internal kernel calls that use random data for
non-crypto things.

> I do not get why you call the implementation crippled? The
> specification is quite thorough and provides well reasoned requirements
> as well as self-test that insure coding mistakes won't end up returning
> non-random values.

Which specification are you talking about exactly? There are loads of
different ones it seems that people wish to follow, so it's hard to
claim that they all are sane :)

> I understand the mistrust vs gov agencies due to past mishaps like the
> Dual-DRBG thing, but we are not talking about something like that in
> this case. NIST is not mandating any specific algorithmic
> implementation, the requirement set forth allow to use a variety of
> different algorithms so that everyone can choose what they think is
> sane.
>
> > Again, just try evolving the existing code to meet the needs that you
> > all have, stop trying to do wholesale reimplementations. Those never
> > succeed, and it's pretty obvious that no one wants a "plugin a random
> > random driver" interface, right?
>
> I think one of the issues is that the number of changes required
> against the current random driver amount essentially to a re-
> implementation. Sure, you can do it as a series of patches that
> transform the current code in something completely different.

That is how kernel development works, it is nothing new.

> And the main question here is, how can we get there, in any case, if
> the maintainer of the random device doesn't even participate in
> discussions, does not pick obvious bug fixes and is simply not engaging
> at all?

What obvious bug fixes have been dropped?

> Your plan requires an active maintainer that guides these changes and
> interact with the people proposing them to negotiate the best outcome.
> But that is not happening so that road seem blocked at the moment.

We need working patches that fit with the kernel development model first
before people can start blaming maintainers :)

I see almost 300 changes accepted for this tiny random.c file over the
years we have had git (17 years). I think that's a very large number of
changes for a 2300 line file that is relied upon by everyone.


thanks,

greg k-h

2021-11-30 17:07:21

by Willy Tarreau

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Tue, Nov 30, 2021 at 04:45:44PM +0100, Greg Kroah-Hartman wrote:
> On Tue, Nov 30, 2021 at 09:31:09AM -0500, Simo Sorce wrote:
> > On Tue, 2021-11-30 at 15:04 +0100, Greg Kroah-Hartman wrote:
> > > Odds are, you REALLY do not want the in-kernel calls to be pulling from
> > > the "random-government-crippled-specification" implementation, right?
> >
> > You really *do* want that.
> > When our customers are mandated to use FIPS certified cryptography,
> > they want to use it for kernel cryptography as well, and in general
> > they want to use a certified randomness source as well.
>
> There are huge numbers of internal kernel calls that use random data for
> non-crypto things.

I think the confusion comes from the use of cryptography to hide the
internal state and provide non-predictable sequences, and not from the
use of this source to perform cryptography elsewhere. But crypto here,
when used, is not a goal but a means. We could call this a "reduction"
function or a "whitening" function. Its importance solely depends on
how much we want to protect the internal state from being guessed, which
first comes back to how long the knowledge of this internal state is
useful. If we'd mix completely independent and unpredictable sources
like cosmic microwave background noise and sea-level beta radiations,
these are constantly renewed, their knowledge doesn't bring anything
and there's no need for crypto to protect them. That's not necessarily
what we're using and we have to deal with more durable source whose
disclosure could have more impact for some time frame, thus would need
some protection.

As such there is probably a broad spectrum between "we must use strong
cryptography on this source hence abide with authorities' decisions" and
"we just need this short-lived state not to be trivially guessable till
the next call". In this case do we *really* care about what crypto
functions are used to hide the internal state ? I guess not really, and
that could possibly be configurable at run time. After all, in practice
the jitter entropy and other sources might add sufficient uncertainty
to complicate analysis of even a weak algorithm and render the internal
state hardly guessable.

> > Your plan requires an active maintainer that guides these changes and
> > interact with the people proposing them to negotiate the best outcome.
> > But that is not happening so that road seem blocked at the moment.
>
> We need working patches that fit with the kernel development model first
> before people can start blaming maintainers :)
>
> I see almost 300 changes accepted for this tiny random.c file over the
> years we have had git (17 years). I think that's a very large number of
> changes for a 2300 line file that is relied upon by everyone.

I'm also having some concerns about this. It seems to me that it's always
difficult to *simplify* what we have and that each time we try to replace
something in that area we end up with multiple versions. Look at the recent
prandom32 stuff for example. We got a new algo used for IP IDs, in a rush,
hoping to generalize it to replace the existing Tausworthe one. I had a look
a few months ago to try to finish the job... hundreds of callers that make
use of the internal state for unit tests :-( Basically unfeasible without
breaking lots of driver I have no idea how to test. So by trying to replace
something we just ended up with two implementations (and if I remember well
there were already a few more, mostly variants of the former).

A replacement ought to be an observation, a conclusion of a work well done,
not a goal. If the changes manage to move everyone in the right direction
and at the end everything is seamlessly replaced for good, that's awesome.
But changing for changing is hard. And if we end up with build time options
to decide between one solution and the other, we fragment the testability :-/

Just my two cents,
Willy

2021-11-30 17:09:02

by Simo Sorce

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Tue, 2021-11-30 at 16:45 +0100, Greg Kroah-Hartman wrote:
> On Tue, Nov 30, 2021 at 09:31:09AM -0500, Simo Sorce wrote:
> > On Tue, 2021-11-30 at 15:04 +0100, Greg Kroah-Hartman wrote:
> > > On Tue, Nov 30, 2021 at 07:24:15AM -0500, Jeffrey Walton wrote:
> > > > On Mon, Nov 29, 2021 at 6:07 PM Greg Kroah-Hartman
> > > > <[email protected]> wrote:
> > > > > ...
> > > > > Sometimes, yes, it is valid to have different implementations for things
> > > > > that do different things in the same area (like filesystems), but for a
> > > > > core function of the kernel, so far the existing random maintainer has
> > > > > not wanted to have multiple implementations. Same goes for other parts
> > > > > of the kernel, it's not specific only to this one very tiny driver.
> > > > >
> > > > > As a counterpoint, we do not allow duplicate drivers that control the
> > > > > same hardware types in the tree. We have tried that in the past and it
> > > > > was a nightmare to support and maintain and just caused massive user
> > > > > confusion as well. One can argue that the random driver is in this same
> > > > > category.
> > > >
> > > > I think an argument could be made that they are different drivers
> > > > since they have different requirements and security goals. I don't
> > > > think it matters where the requirements came from, whether it was ad
> > > > hoc from the developer, NIST, KISA, CRYPTREC, NESSIE, or another
> > > > organization.
> > > >
> > > > Maybe the problem is with the name of the driver? Perhaps the current
> > > > driver should be named random-linux, Stephan's driver should be named
> > > > random-nist, and the driver should be wired up based on a user's
> > > > selection. That should sidestep the problems associated with the
> > > > "duplicate drivers" policy.
> > >
> > > The "problem" here is that the drivers/char/random.c file has three users,
> > > the userspace /dev/random and syscall api, the in-kernel "here's some
> > > entropy for the random core to use" api, and the in-kernel "give me some
> > > random data" api.
> > >
> > > Odds are, you REALLY do not want the in-kernel calls to be pulling from
> > > the "random-government-crippled-specification" implementation, right?
> >
> > You really *do* want that.
> > When our customers are mandated to use FIPS certified cryptography,
> > they want to use it for kernel cryptography as well, and in general
> > they want to use a certified randomness source as well.
>
> There are huge numbers of internal kernel calls that use random data for
> non-crypto things.

Sure, but it makes little sense to use different random implementations
unless there are specific issues in terms of performance. It is also
not always easy to establish if a certain use of random numbers is
actually security relevant, may be context dependent, so it is
generally safer to just use the certified implementation for everything
if possible.

> > I do not get why you call the implementation crippled? The
> > specification is quite thorough and provides well reasoned requirements
> > as well as self-test that insure coding mistakes won't end up returning
> > non-random values.
>
> Which specification are you talking about exactly? There are loads of
> different ones it seems that people wish to follow, so it's hard to
> claim that they all are sane :)

Well, given I am interested primarily in FIPS certifications I was
referring specifically to SP800-90A/B/C:
https://csrc.nist.gov/publications/detail/sp/800-90a/rev-1/final
https://csrc.nist.gov/publications/detail/sp/800-90b/final
https://csrc.nist.gov/publications/detail/sp/800-90c/draft

> > I understand the mistrust vs gov agencies due to past mishaps like the
> > Dual-DRBG thing, but we are not talking about something like that in
> > this case. NIST is not mandating any specific algorithmic
> > implementation, the requirement set forth allow to use a variety of
> > different algorithms so that everyone can choose what they think is
> > sane.
> >
> > > Again, just try evolving the existing code to meet the needs that you
> > > all have, stop trying to do wholesale reimplementations. Those never
> > > succeed, and it's pretty obvious that no one wants a "plugin a random
> > > random driver" interface, right?
> >
> > I think one of the issues is that the number of changes required
> > against the current random driver amount essentially to a re-
> > implementation. Sure, you can do it as a series of patches that
> > transform the current code in something completely different.
>
> That is how kernel development works, it is nothing new.
>
> > And the main question here is, how can we get there, in any case, if
> > the maintainer of the random device doesn't even participate in
> > discussions, does not pick obvious bug fixes and is simply not engaging
> > at all?
>
> What obvious bug fixes have been dropped?

Stephan posted you the link a few days ago for one of the examples.
If you look at the last year you also will see that multiple patches
have gone in w/o the maintainer interacting, which is fine for obvious
stuff, but does not work for stuff that requires more feedback.

> > Your plan requires an active maintainer that guides these changes and
> > interact with the people proposing them to negotiate the best outcome.
> > But that is not happening so that road seem blocked at the moment.
>
> We need working patches that fit with the kernel development model first
> before people can start blaming maintainers :)

This is a Catch-22, the maintainer is mum in what would be acceptable,
and whenever there are patches sent, there is no feedback on whether
they are acceptable or not. It's not like I like blaming anyone, but it
would be nice to have at least one word that gives a direction to
follow that the maintainer is willing to then engage with and review or
at least accept with proper third party review.

> I see almost 300 changes accepted for this tiny random.c file over the
> years we have had git (17 years). I think that's a very large number of
> changes for a 2300 line file that is relied upon by everyone.

Seem like a lot more are desired too :-)

Simo.

--
Simo Sorce
RHEL Crypto Team
Red Hat, Inc





2021-11-30 18:16:00

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Tue, Nov 30, 2021 at 04:45:44PM +0100, Greg Kroah-Hartman wrote:
> > And the main question here is, how can we get there, in any case, if
> > the maintainer of the random device doesn't even participate in
> > discussions, does not pick obvious bug fixes and is simply not engaging
> > at all?
>
> What obvious bug fixes have been dropped?
>

The RNDRESEEDCRNG ioctl was totally broken, and I sent out a patch to fix it
which was ignored for months:
https://lore.kernel.org/linux-crypto/[email protected]/

Reminders didn't help:

First ping: https://lore.kernel.org/linux-crypto/[email protected]/
Second ping: https://lore.kernel.org/linux-crypto/[email protected]/
Third ping: https://lore.kernel.org/linux-crypto/[email protected]/
Fourth ping: https://lore.kernel.org/linux-crypto/X%[email protected]/
Resent to Andrew Morton: https://lore.kernel.org/linux-crypto/[email protected]/
Pinged Andrew: https://lore.kernel.org/linux-crypto/[email protected]/

Finally *you* took the patch: https://lore.kernel.org/linux-crypto/[email protected]/

Here's another random.c bug fix which was ignored, this one for 6 months before
Herbert Xu finally took it through the crypto tree:
https://lore.kernel.org/linux-crypto/[email protected]/

Here's a dead code cleanup which was ignored for 6 months before being taken by
Herbert Xu through the crypto tree:
https://lore.kernel.org/linux-crypto/[email protected]/

Here's a patch to random.c which was taken by the arm64 maintainers due to being
ignored by the random.c maintainer:
https://lore.kernel.org/lkml/[email protected]/

So unfortunately, as far as I can tell, Ted is not maintaining random.c anymore.

- Eric

2021-11-30 18:39:44

by Jason A. Donenfeld

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Tue, Nov 30, 2021 at 1:16 PM Eric Biggers <[email protected]> wrote:
> So unfortunately, as far as I can tell, Ted is not maintaining random.c anymore.

I am happy to step up here. Feel free to CC me on random.c fixes and
I'll review them promptly.

Jason

2021-11-30 19:43:04

by Simo Sorce

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Tue, 2021-11-30 at 13:39 -0500, Jason A. Donenfeld wrote:
> On Tue, Nov 30, 2021 at 1:16 PM Eric Biggers <[email protected]> wrote:
> > So unfortunately, as far as I can tell, Ted is not maintaining random.c anymore.
>
> I am happy to step up here. Feel free to CC me on random.c fixes and
> I'll review them promptly.

Jason,
are you also volunteering to review the patches needed to reach
compliance with the NIST documents I mentioned in the thread?

Simo.

--
Simo Sorce
RHEL Crypto Team
Red Hat, Inc





2021-12-01 16:03:15

by Jason A. Donenfeld

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Hi Simo,

I think various folks have said this during the various discussions on this
topic over the years, in addition to myself, but I suppose I'll reiterate my
general views on FIPS in this context.

FIPS is about compliance and certification. From a cryptographic point of
view, there might be some good ideas, some dated ideas, some superfluous but
harmless ideas, and so forth. But the reason that you want it for your
customers is because you think your product will become more valuable or
useful to customers if it checks that green compliance checkbox. I don't think
we disagree about this being the motivation.

Now typically the kernel interoperates with lots of things and implements many
different specifications. It supports scores of network protocols, IPsec
cipher suites, USB quirks, SCSI hacks, you name it. The implementation of
these drivers is always up to the author and hopefully kernel developers at
large do the best job they can with the implementation, but the hardware or
protocol they're interfacing with is not up to the author, by virtue of it
being external to the kernel. It's not like instantiating IPsec with single
DES and MD4, or SM3 and SM4, etc. is so great, and it's not like the
compendium of brilliant hacks in drivers/usb/host/pci-quirks.c is so great
either. But these things all exist to talk to something *outside* of the
kernel, and so we grit our teeth, and as I said, do the best we can to
implement it well.

But the RNG isn't like that. In fact, the RNG is logically *required* to be
not anything like that: it returns random bytes, and they must not have any
distinguishing quality with other random bytes; otherwise we have a serious
problem that needs fixing. And so, we carry things out according to the usual
kernel developer mindset: we implement it as best as we can, using the best
algorithms we can find, in a way most suitable for the kernel.

Then FIPS comes along and starts dictating things about *how* we implement it,
and those things it dictates might not be exactly the same as what we would
would be doing when doing best that we can, using the best algorithms we can
find, and in the most suitable way for the kernel. And so it would seem that
the goal of implementing the RNG as best as we can might potentially be at
odds with the goal of getting that green compliance checkbox, because that
checkbox oversteps its bounds a bit.

That's not to say, of course, that we shouldn't accept input on how we
implement our algorithms from elsewhere. On the contrary, I think random.c has
a *lot* to gain from incorporating newer ideas, and that the formalism and
guidance from academic cryptographers is less "academic" than it once was and
much more real world, implementable, and suitable for our uses. But, again,
incorporating new ideas and accepting input on how to improve our code is very
much not the same thing as following the FIPS laundry list for that green
compliance checkbox. Maybe some parts do overlap -- and I'd love patches that
improve the code alongside compelling cryptographic arguments -- but, again,
we're talking about compliance here, and not a more welcome, "hey check out
this document I found with a bunch of great ideas we should implement."

I would like the kernel to have an excellent CSPRNG, from a cryptographic
point of view, from a performance point of view, from an API point of view. I
think these motivations are consistent with how the kernel is generally
developed. And I think front loading the motivations with an external
compliance goal greatly deviates and even detracts from the way the kernel is
generally developed.

Now the above is somewhat negative on FIPS, but the question can still be
posed: does FIPS have a path forward in the RNG in the kernel? It's obviously
not a resounding "yes", but I don't think it's a totally certain "no" either.
It might be possible to find some wiggle room. I'm not saying that it is
certainly possible to do that, but it might be.

Specifically, I think that if you change your perspective from, "how can we
change the algorithms of the RNG to be FIPS" to "how can we bend FIPS within
its limits so that having what customers want would minimally impact the
quality of the RNG implementation or introduce undue maintenance burdens."
This means: not refactoring the RNG into some large abstraction layer that's
pluggable and supports multiple different implementations, not rewriting the
world in a massive patchset, not adding clutter. Instead, perhaps there's a
very, very minimal set of things that can be done that would be considerably
less controversial. That will probably require from you and other FIPS
enthusiasts some study and discussion at what the truly most minimal set of
things required are to get you that green compliance checkbox. And hey --
maybe it's still way too much and it doesn't work out here. But maybe it's not
that much, or, as Greg suggested, maybe it winds up that your needs are
actually satisfied just fine by something in userspace or userspace-adjacent.

So I don't know whether the FIPS has a path forward here, but if it does, I
think the above is the general shape it would take. And in the mean time, I'm
of course open to reviewing patches that improve the RNG in a cryptographic or
algorithmic sense, rather than a purely compliance one.

Hopefully that helps you understand more about where we're coming from.

Regards,
Jason

2021-12-01 17:19:41

by Simo Sorce

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Hi Jason,
thanks for your reply, I appreciate when there is a clear exchange of
ideas.

Comment inline.

On Wed, 2021-12-01 at 11:02 -0500, Jason A. Donenfeld wrote:
> Hi Simo,
>
> I think various folks have said this during the various discussions on this
> topic over the years, in addition to myself, but I suppose I'll reiterate my
> general views on FIPS in this context.
>
> FIPS is about compliance and certification. From a cryptographic point of
> view, there might be some good ideas, some dated ideas, some superfluous but
> harmless ideas, and so forth. But the reason that you want it for your
> customers is because you think your product will become more valuable or
> useful to customers if it checks that green compliance checkbox. I don't think
> we disagree about this being the motivation.

I have to say that although there is clearly a great deal of "because
we need the certification" I do not fully agree that FIPS is just a
checkbox to be ticked for me. Of course for customers that do not care
that much it is, and it is a required one. However having worked a lot
on this I can tell you there is actually real cryptographic value in
the requirements FIPS introduced over the years. If anything my
complaint is that the list of accepted constructs is restrictive, but
other than that most of the requirements have real world sense, and the
certification process do uncover issue that otherwise would linger as
the testing is rather thorough.

> Now typically the kernel interoperates with lots of things and implements many
> different specifications. It supports scores of network protocols, IPsec
> cipher suites, USB quirks, SCSI hacks, you name it. The implementation of
> these drivers is always up to the author and hopefully kernel developers at
> large do the best job they can with the implementation, but the hardware or
> protocol they're interfacing with is not up to the author, by virtue of it
> being external to the kernel. It's not like instantiating IPsec with single
> DES and MD4, or SM3 and SM4, etc. is so great, and it's not like the
> compendium of brilliant hacks in drivers/usb/host/pci-quirks.c is so great
> either. But these things all exist to talk to something *outside* of the
> kernel, and so we grit our teeth, and as I said, do the best we can to
> implement it well.
>
> But the RNG isn't like that. In fact, the RNG is logically *required* to be
> not anything like that: it returns random bytes, and they must not have any
> distinguishing quality with other random bytes; otherwise we have a serious
> problem that needs fixing. And so, we carry things out according to the usual
> kernel developer mindset: we implement it as best as we can, using the best
> algorithms we can find, in a way most suitable for the kernel.
>
> Then FIPS comes along and starts dictating things about *how* we implement it,
> and those things it dictates might not be exactly the same as what we would
> would be doing when doing best that we can, using the best algorithms we can
> find, and in the most suitable way for the kernel. And so it would seem that
> the goal of implementing the RNG as best as we can might potentially be at
> odds with the goal of getting that green compliance checkbox, because that
> checkbox oversteps its bounds a bit.

Well I think most of the requirements are sane practices, hopefully
controversial stuff will be minimal. I fully believe we can work to
have the best we can as well as having it FIPS compliant, because the
intent of FIPS requirements here is exactly to have the best guarantees
for randomness.

> That's not to say, of course, that we shouldn't accept input on how we
> implement our algorithms from elsewhere. On the contrary, I think random.c has
> a *lot* to gain from incorporating newer ideas, and that the formalism and
> guidance from academic cryptographers is less "academic" than it once was and
> much more real world, implementable, and suitable for our uses. But, again,
> incorporating new ideas and accepting input on how to improve our code is very
> much not the same thing as following the FIPS laundry list for that green
> compliance checkbox. Maybe some parts do overlap -- and I'd love patches that
> improve the code alongside compelling cryptographic arguments -- but, again,
> we're talking about compliance here, and not a more welcome, "hey check out
> this document I found with a bunch of great ideas we should implement."

I happen to think quite a few of the requirements are actually good
ideas to implement to improve the guarantees of randomness, but there
may definitely be contentious ideas of what is "good" and what is an
"arbitrary request".

> I would like the kernel to have an excellent CSPRNG, from a cryptographic
> point of view, from a performance point of view, from an API point of view. I
> think these motivations are consistent with how the kernel is generally
> developed. And I think front loading the motivations with an external
> compliance goal greatly deviates and even detracts from the way the kernel is
> generally developed.

I would agree is compliance meant arbitrary/capricious requirements.
Hopefully most of the requirements are reasonable and we can find a
reasonable way to fulfill them.

> Now the above is somewhat negative on FIPS, but the question can still be
> posed: does FIPS have a path forward in the RNG in the kernel? It's obviously
> not a resounding "yes", but I don't think it's a totally certain "no" either.
> It might be possible to find some wiggle room. I'm not saying that it is
> certainly possible to do that, but it might be.
>
> Specifically, I think that if you change your perspective from, "how can we
> change the algorithms of the RNG to be FIPS" to "how can we bend FIPS within
> its limits so that having what customers want would minimally impact the
> quality of the RNG implementation or introduce undue maintenance burdens."

We always operate with this mindset to be quite frank. The kernel is
just one of many crypto modules we certify, and we always try to find
the least invasive options in all the crypto modules we certify. We
also normally try to get all changes upstream because we think they are
a benefit to everyone.

> This means: not refactoring the RNG into some large abstraction layer that's
> pluggable and supports multiple different implementations, not rewriting the
> world in a massive patchset, not adding clutter.

I think the reason to propose this is to deal with cases where the
kernel community and the FIPS requirement diverge. A pluggable system
allows to provide a downstream module and in general will provide clear
entry-points where to apply downstream patches that minimally deviate
from the general structure.

Unfortunately, regardless of what the kernel community find of its
liking we have a business need to provide FIPS certifications to our
customers. So for us it is also a matter of pragmatically reducing to
the maximum extent possible any necessary deviation from upstream
kernels.

> Instead, perhaps there's a
> very, very minimal set of things that can be done that would be considerably
> less controversial. That will probably require from you and other FIPS
> enthusiasts some study and discussion at what the truly most minimal set of
> things required are to get you that green compliance checkbox.

As long as there is actual feedback and a willingness to reach a
compromise on both sides when a change does not materially cause
issues, I think this is certainly reasonable.

> And hey --
> maybe it's still way too much and it doesn't work out here. But maybe it's not
> that much, or, as Greg suggested, maybe it winds up that your needs are
> actually satisfied just fine by something in userspace or userspace-adjacent.

Unfortunately userspace is not an option for kernel's own cryptography.

> So I don't know whether the FIPS has a path forward here, but if it does, I
> think the above is the general shape it would take. And in the mean time, I'm
> of course open to reviewing patches that improve the RNG in a cryptographic or
> algorithmic sense, rather than a purely compliance one.

Hopefully we can take both into account.

> Hopefully that helps you understand more about where we're coming from.

It does,
thanks.

Simo.

--
Simo Sorce
RHEL Crypto Team
Red Hat, Inc





2021-12-01 17:55:49

by Boris Krasnovskiy

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Hi Jason, Greg,

a lot of the issues LRNG address are related to RNG and FIPS on embedded/IoT devices.

The problem we have is that /dev/random as it stands today in many cases does not generate enough entropy to support cryptography on embedded/IoT devices. Embedded devices in most cases have limited interrupt and IO activity, they do have in many cases aggressive power management where device is up and running few seconds at a time and fallbacks into suspend mode, this is done to preserve power (battery and otherwise), and now such operation even the legal mandate in EU (we are going green).

What options do we have here:

Use hardware random number if CPU supports it. Yes, some people do not trust it, but it's better than nothing. /dev/random currently does not allow to mix in Hardware RNG unless quality parameter is set, and none of the kernel Hardware RNG have it set out of the box.

Not all CPUs have hardware RNG to use or have non-compliant Hardware RNG (which is after 90C was adopted most of the older ones, 90C requires runtime test on raw entropy which is not exposed outside of the IC), what to do now? This is one of the areas where Jitter RNG comes in. It has fast entropy collection, meets FIPS requirements and could be run on any CPU, provided it has High Resolution Timer, does not require storing seed on the file system that is prohibited by FIPS. There is no option as of today to mix in or flood Jitter entropy into /dev/random inside kernel.

I am aware that there are userspace daemons that take entropy from Hardware RNG or Jitter and feed into /dev/random, but is it really the best approach?
Now let get into FIPS on such systems.

I hoped I explained earlier why existing /dev/random is unusable.
If userspace solution to /dev/random is used, one has to demonstrate that entropy fed by userspace daemon floods /dev/random to the point where other sources of entropy statistically does not matter. That increases cost of FIPS certification by about 30% last time we checked.

This is why it would help a lot if one can choose from kernel configuration which RNG is appropriate for the system at hand, and this exactly what LRNG does.

Thank you,
Boris
THIS MESSAGE, ANY ATTACHMENT(S), AND THE INFORMATION CONTAINED HEREIN MAY BE PROPRIETARY TO LAIRD CONNECTIVITY, INC. AND/OR ANOTHER PARTY, AND MAY FURTHER BE INTENDED TO BE KEPT CONFIDENTIAL. IF YOU ARE NOT THE INTENDED RECIPIENT, PLEASE DELETE THE EMAIL AND ANY ATTACHMENTS, AND IMMEDIATELY NOTIFY THE SENDER BY RETURN EMAIL. THIS MESSAGE AND ITS CONTENTS ARE THE PROPERTY OF LAIRD CONNECTIVITY, INC. AND MAY NOT BE REPRODUCED OR USED WITHOUT THE EXPRESS WRITTEN CONSENT OF LAIRD CONNECTIVITY, INC.

2021-12-01 18:05:15

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Wed, Dec 01, 2021 at 05:55:32PM +0000, Boris Krasnovskiy wrote:
> THIS MESSAGE, ANY ATTACHMENT(S), AND THE INFORMATION CONTAINED HEREIN MAY BE PROPRIETARY TO LAIRD CONNECTIVITY, INC. AND/OR ANOTHER PARTY, AND MAY FURTHER BE INTENDED TO BE KEPT CONFIDENTIAL. IF YOU ARE NOT THE INTENDED RECIPIENT, PLEASE DELETE THE EMAIL AND ANY ATTACHMENTS, AND IMMEDIATELY NOTIFY THE SENDER BY RETURN EMAIL. THIS MESSAGE AND ITS CONTENTS ARE THE PROPERTY OF LAIRD CONNECTIVITY, INC. AND MAY NOT BE REPRODUCED OR USED WITHOUT THE EXPRESS WRITTEN CONSENT OF LAIRD CONNECTIVITY, INC.

Now deleted.

2021-12-01 18:05:26

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Wed, Dec 01, 2021 at 05:19:37PM +0000, Boris Krasnovskiy wrote:
> THIS MESSAGE, ANY ATTACHMENT(S), AND THE INFORMATION CONTAINED HEREIN MAY BE PROPRIETARY TO LAIRD CONNECTIVITY, INC. AND/OR ANOTHER PARTY, AND MAY FURTHER BE INTENDED TO BE KEPT CONFIDENTIAL. IF YOU ARE NOT THE INTENDED RECIPIENT, PLEASE DELETE THE EMAIL AND ANY ATTACHMENTS, AND IMMEDIATELY NOTIFY THE SENDER BY RETURN EMAIL. THIS MESSAGE AND ITS CONTENTS ARE THE PROPERTY OF LAIRD CONNECTIVITY, INC. AND MAY NOT BE REPRODUCED OR USED WITHOUT THE EXPRESS WRITTEN CONSENT OF LAIRD CONNECTIVITY, INC.

Now deleted.

2021-12-01 18:25:11

by Jason A. Donenfeld

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Wed, Dec 1, 2021 at 12:19 PM Simo Sorce <[email protected]> wrote:
> that much it is, and it is a required one. However having worked a lot
> on this I can tell you there is actually real cryptographic value in
> the requirements FIPS introduced over the years
> Well I think most of the requirements are sane practices, hopefully
> controversial stuff will be minimal.
> I happen to think quite a few of the requirements are actually good
> ideas to implement to improve the guarantees of randomness

If you think there are good ways to improve the RNG, of course send
patches for this, justifying why, taking into account recent research
into the topic you wish to patch, etc. Don't write, "because FIPS";
instead argue rationale for each patch. And if you _do_ feel the need
to appeal to authority, perhaps links to the various eprint papers you
consulted would be worthwhile. Preferably you're able to do this in a
small, incremental way, with small standalone patchsets, instead of
gigantic series.

2021-12-01 18:29:40

by Jason A. Donenfeld

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Wed, Dec 1, 2021 at 12:19 PM Simo Sorce <[email protected]> wrote:
> Unfortunately userspace is not an option for kernel's own cryptography.

I'm actually sort of curious to learn which specific uses of
get_random_bytes you're concerned about. ECC keygen? What else?

2021-12-02 00:26:21

by Jeffrey Walton

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Wed, Dec 1, 2021 at 1:25 PM Jason A. Donenfeld <[email protected]> wrote:
>
> On Wed, Dec 1, 2021 at 12:19 PM Simo Sorce <[email protected]> wrote:
> > that much it is, and it is a required one. However having worked a lot
> > on this I can tell you there is actually real cryptographic value in
> > the requirements FIPS introduced over the years
> > Well I think most of the requirements are sane practices, hopefully
> > controversial stuff will be minimal.
> > I happen to think quite a few of the requirements are actually good
> > ideas to implement to improve the guarantees of randomness
>
> If you think there are good ways to improve the RNG, of course send
> patches for this, justifying why, taking into account recent research
> into the topic you wish to patch, etc. Don't write, "because FIPS";
> instead argue rationale for each patch. And if you _do_ feel the need
> to appeal to authority, perhaps links to the various eprint papers you
> consulted would be worthwhile. Preferably you're able to do this in a
> small, incremental way, with small standalone patchsets, instead of
> gigantic series.

I may be parsing things incorrectly, but you seem to be rejecting the
NIST requirements, and then positioning your personal opinion as
superior. It sounds like one authority is being replaced by another.
Perhaps I am missing something.

I am also guessing you've never read the relevant NIST documents. The
documents state the security goals and provide the steps to achieve
them in an implementation.

Jeff

2021-12-02 07:12:46

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Wed, Dec 01, 2021 at 07:24:43PM -0500, Jeffrey Walton wrote:
> On Wed, Dec 1, 2021 at 1:25 PM Jason A. Donenfeld <[email protected]> wrote:
> >
> > On Wed, Dec 1, 2021 at 12:19 PM Simo Sorce <[email protected]> wrote:
> > > that much it is, and it is a required one. However having worked a lot
> > > on this I can tell you there is actually real cryptographic value in
> > > the requirements FIPS introduced over the years
> > > Well I think most of the requirements are sane practices, hopefully
> > > controversial stuff will be minimal.
> > > I happen to think quite a few of the requirements are actually good
> > > ideas to implement to improve the guarantees of randomness
> >
> > If you think there are good ways to improve the RNG, of course send
> > patches for this, justifying why, taking into account recent research
> > into the topic you wish to patch, etc. Don't write, "because FIPS";
> > instead argue rationale for each patch. And if you _do_ feel the need
> > to appeal to authority, perhaps links to the various eprint papers you
> > consulted would be worthwhile. Preferably you're able to do this in a
> > small, incremental way, with small standalone patchsets, instead of
> > gigantic series.
>
> I may be parsing things incorrectly, but you seem to be rejecting the
> NIST requirements, and then positioning your personal opinion as
> superior. It sounds like one authority is being replaced by another.
> Perhaps I am missing something.
>
> I am also guessing you've never read the relevant NIST documents. The
> documents state the security goals and provide the steps to achieve
> them in an implementation.

Ok, I think this thread has gone on long enough without any real
patches.

Please, if you want to support NIST, or any other type of thing, submit
patches that implement what you think will help achieve this. Absent of
that, we have no idea what NIST or any other random document aims to
require or wish.

thanks,

greg k-h

2021-12-02 16:02:21

by John Haxby

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator



> On 2 Dec 2021, at 07:12, Greg Kroah-Hartman <[email protected]> wrote:
>
> On Wed, Dec 01, 2021 at 07:24:43PM -0500, Jeffrey Walton wrote:
>> On Wed, Dec 1, 2021 at 1:25 PM Jason A. Donenfeld <[email protected]> wrote:
>>>
>>> On Wed, Dec 1, 2021 at 12:19 PM Simo Sorce <[email protected]> wrote:
>>>> that much it is, and it is a required one. However having worked a lot
>>>> on this I can tell you there is actually real cryptographic value in
>>>> the requirements FIPS introduced over the years
>>>> Well I think most of the requirements are sane practices, hopefully
>>>> controversial stuff will be minimal.
>>>> I happen to think quite a few of the requirements are actually good
>>>> ideas to implement to improve the guarantees of randomness
>>>
>>> If you think there are good ways to improve the RNG, of course send
>>> patches for this, justifying why, taking into account recent research
>>> into the topic you wish to patch, etc. Don't write, "because FIPS";
>>> instead argue rationale for each patch. And if you _do_ feel the need
>>> to appeal to authority, perhaps links to the various eprint papers you
>>> consulted would be worthwhile. Preferably you're able to do this in a
>>> small, incremental way, with small standalone patchsets, instead of
>>> gigantic series.
>>
>> I may be parsing things incorrectly, but you seem to be rejecting the
>> NIST requirements, and then positioning your personal opinion as
>> superior. It sounds like one authority is being replaced by another.
>> Perhaps I am missing something.
>>
>> I am also guessing you've never read the relevant NIST documents. The
>> documents state the security goals and provide the steps to achieve
>> them in an implementation.
>
> Ok, I think this thread has gone on long enough without any real
> patches.
>
> Please, if you want to support NIST, or any other type of thing, submit
> patches that implement what you think will help achieve this. Absent of
> that, we have no idea what NIST or any other random document aims to
> require or wish.


Part of the problem here is that NIST (and the concomitant fips certification) is a moving target. A couple of years ago, we were fine. Today's requirements are different, tomorrow's will be different again. Today's requirements being different are what resulted in the small patch I mentioned earlier.

You suggested, Greg, that I submit that and see what happens. I can take a hint :) so I'm working on that as a possible way forward to decouple things a bit without too much churn.

jch



>
> thanks,
>
> greg k-h


Attachments:
signature.asc (228.00 B)
Message signed with OpenPGP

2021-12-04 09:54:10

by Sandy Harris

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Greg Kroah-Hartman <[email protected]> wrote:

> Also, why does any of this have to be in the kernel at all?

The kernel has had random(4) since Ted invented it sometime in
the 90s. There's no question it's a good idea; that's why all the BSDs
& some others have copied it. The only questions here are whether
it could be made FIPS compliant & whether it should be.

> If FIPS requires a deterministic random number generator
> that will not allow entropy to be acquired from hardware
> or external inputs,

It doesn't require that at all; in fact their DRNG design
requires an external source of random bits. However, it
requires that the source be certified & that would be a
problem for us. Intel & others might be able to get their
random number instructions certified and vendors of
crypto or SOC chips might get theirs certified, but the
kernel community could not do that.

I think the kernel's entropy collection routines are good
enough that they could, in principle, be certified, but
that would involve some work & considerable money.

> why does the
> kernel care at all? Just write a fips_random.so library and get it
> certified and have any userspace code that cares about such a crazy
> thing to use that instead.

That does not solve the problem. The library would
also need a certified source of random inputs, so
to get it certified you'd have to get something else
certified first -- random(4), an instruction or a hardware
rng.

2021-12-10 01:44:03

by Marcelo Henrique Cerri

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Wed, Dec 01, 2021 at 11:02:38AM -0500, Jason A. Donenfeld wrote:
> Hi Simo,
>
> I think various folks have said this during the various discussions on this
> topic over the years, in addition to myself, but I suppose I'll reiterate my
> general views on FIPS in this context.
>
> FIPS is about compliance and certification. From a cryptographic point of
> view, there might be some good ideas, some dated ideas, some superfluous but
> harmless ideas, and so forth. But the reason that you want it for your
> customers is because you think your product will become more valuable or
> useful to customers if it checks that green compliance checkbox. I don't think
> we disagree about this being the motivation.
>
> Now typically the kernel interoperates with lots of things and implements many
> different specifications. It supports scores of network protocols, IPsec
> cipher suites, USB quirks, SCSI hacks, you name it. The implementation of
> these drivers is always up to the author and hopefully kernel developers at
> large do the best job they can with the implementation, but the hardware or
> protocol they're interfacing with is not up to the author, by virtue of it
> being external to the kernel. It's not like instantiating IPsec with single
> DES and MD4, or SM3 and SM4, etc. is so great, and it's not like the
> compendium of brilliant hacks in drivers/usb/host/pci-quirks.c is so great
> either. But these things all exist to talk to something *outside* of the
> kernel, and so we grit our teeth, and as I said, do the best we can to
> implement it well.
>
> But the RNG isn't like that. In fact, the RNG is logically *required* to be
> not anything like that: it returns random bytes, and they must not have any
> distinguishing quality with other random bytes; otherwise we have a serious
> problem that needs fixing. And so, we carry things out according to the usual
> kernel developer mindset: we implement it as best as we can, using the best
> algorithms we can find, in a way most suitable for the kernel.
>
> Then FIPS comes along and starts dictating things about *how* we implement it,
> and those things it dictates might not be exactly the same as what we would
> would be doing when doing best that we can, using the best algorithms we can
> find, and in the most suitable way for the kernel. And so it would seem that
> the goal of implementing the RNG as best as we can might potentially be at
> odds with the goal of getting that green compliance checkbox, because that
> checkbox oversteps its bounds a bit.
>
> That's not to say, of course, that we shouldn't accept input on how we
> implement our algorithms from elsewhere. On the contrary, I think random.c has
> a *lot* to gain from incorporating newer ideas, and that the formalism and
> guidance from academic cryptographers is less "academic" than it once was and
> much more real world, implementable, and suitable for our uses. But, again,
> incorporating new ideas and accepting input on how to improve our code is very
> much not the same thing as following the FIPS laundry list for that green
> compliance checkbox. Maybe some parts do overlap -- and I'd love patches that
> improve the code alongside compelling cryptographic arguments -- but, again,
> we're talking about compliance here, and not a more welcome, "hey check out
> this document I found with a bunch of great ideas we should implement."
>
> I would like the kernel to have an excellent CSPRNG, from a cryptographic
> point of view, from a performance point of view, from an API point of view. I
> think these motivations are consistent with how the kernel is generally
> developed. And I think front loading the motivations with an external
> compliance goal greatly deviates and even detracts from the way the kernel is
> generally developed.
>
> Now the above is somewhat negative on FIPS, but the question can still be
> posed: does FIPS have a path forward in the RNG in the kernel? It's obviously
> not a resounding "yes", but I don't think it's a totally certain "no" either.
> It might be possible to find some wiggle room. I'm not saying that it is
> certainly possible to do that, but it might be.
>
> Specifically, I think that if you change your perspective from, "how can we
> change the algorithms of the RNG to be FIPS" to "how can we bend FIPS within
> its limits so that having what customers want would minimally impact the
> quality of the RNG implementation or introduce undue maintenance burdens."
> This means: not refactoring the RNG into some large abstraction layer that's
> pluggable and supports multiple different implementations, not rewriting the
> world in a massive patchset, not adding clutter. Instead, perhaps there's a
> very, very minimal set of things that can be done that would be considerably
> less controversial. That will probably require from you and other FIPS
> enthusiasts some study and discussion at what the truly most minimal set of
> things required are to get you that green compliance checkbox. And hey --
> maybe it's still way too much and it doesn't work out here. But maybe it's not
> that much, or, as Greg suggested, maybe it winds up that your needs are
> actually satisfied just fine by something in userspace or userspace-adjacent.
>
> So I don't know whether the FIPS has a path forward here, but if it does, I
> think the above is the general shape it would take. And in the mean time, I'm
> of course open to reviewing patches that improve the RNG in a cryptographic or
> algorithmic sense, rather than a purely compliance one.

Hi, Jason. How do you think we could approach that then?

Are you willing to discuss the FIPS 140-3 requirements that random.c
doesn't currently meet so we can dive deeper on how we could implement
them in a way that would improve the kernel other then simply
providing compliance to FIPS?

I believe that several requirements would be beneficial to random.c
(ie, health test, oversampling, entropy data collection). But so far
we lack proper direction on how to proceed and it would be better for
us to have a clear notion of what could be accepted before putting
more effort on yet another patch set.

I believe all the distros are interested in making progress on that,
but without a general guidance it makes very hard for us to
collaborate and we end up in the current situation in which each
distro is carrying its own "hack", as Simo mentioned before. Canonical
is in the same situation as the other distros and we are carrying an
workaround to wire up the crypto DRBG to random.c in order to archive
compliance.

We could also concentrate all the discussion in the linux-crypto
mailing list to facilitate this process, since right now I believe the
MAINTAINERS file doesn't have a specific mailing list associate to
random.c

>
> Hopefully that helps you understand more about where we're coming from.
>
> Regards,
> Jason

--
Regards,
Marcelo


Attachments:
(No filename) (6.77 kB)
signature.asc (659.00 B)
Download all attachments

2021-12-10 06:46:37

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Thu, Dec 09, 2021 at 10:43:37PM -0300, Marcelo Henrique Cerri wrote:
> Hi, Jason. How do you think we could approach that then?
>
> Are you willing to discuss the FIPS 140-3 requirements that random.c
> doesn't currently meet so we can dive deeper on how we could implement
> them in a way that would improve the kernel other then simply
> providing compliance to FIPS?

Discussing things doesn't usually work well. Let's see some working
patches first, that solve problems that you have with the current random
code, and we can go from there.

Again, like any other kernel patch submission, nothing new here at all.

> I believe all the distros are interested in making progress on that,
> but without a general guidance it makes very hard for us to
> collaborate and we end up in the current situation in which each
> distro is carrying its own "hack", as Simo mentioned before. Canonical
> is in the same situation as the other distros and we are carrying an
> workaround to wire up the crypto DRBG to random.c in order to archive
> compliance.

If everyone seems to think their patches are hacks, and are not worthy
of being submitted, then why do they think that somehow they are viable
for their users that are actually using them?

{sigh}

greg k-h

2021-12-10 09:30:29

by Marcelo Henrique Cerri

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Fri, Dec 10, 2021 at 07:46:24AM +0100, Greg Kroah-Hartman wrote:
> On Thu, Dec 09, 2021 at 10:43:37PM -0300, Marcelo Henrique Cerri wrote:
> > Hi, Jason. How do you think we could approach that then?
> >
> > Are you willing to discuss the FIPS 140-3 requirements that random.c
> > doesn't currently meet so we can dive deeper on how we could implement
> > them in a way that would improve the kernel other then simply
> > providing compliance to FIPS?
>
> Discussing things doesn't usually work well. Let's see some working
> patches first, that solve problems that you have with the current random
> code, and we can go from there.
>
> Again, like any other kernel patch submission, nothing new here at all.

Hi, Greg. I understand your point but we had plenty of patch
submissions already from Stephan, Nicolai and others and that didn't
work. So I am expecting that anybody taking over as the random.c
maintainer can at least provide some direction on that.

>
> > I believe all the distros are interested in making progress on that,
> > but without a general guidance it makes very hard for us to
> > collaborate and we end up in the current situation in which each
> > distro is carrying its own "hack", as Simo mentioned before. Canonical
> > is in the same situation as the other distros and we are carrying an
> > workaround to wire up the crypto DRBG to random.c in order to archive
> > compliance.
>
> If everyone seems to think their patches are hacks, and are not worthy
> of being submitted, then why do they think that somehow they are viable
> for their users that are actually using them?

Because although some people dislike it, FIPS is still a requirement
for many users. That's the reality and that will not change just
because there are some resistance against it.

The patches that distros are carrying are hacks because they try to
minimize risks while keeping the code as close as possible to
upstream. But that has several drawbacks, such as performance, limited
entropy sources an so on, that to me makes them not suitable for
upstream.

>
> {sigh}
>
> greg k-h

--
Regards,
Marcelo


Attachments:
(No filename) (2.07 kB)
signature.asc (659.00 B)
Download all attachments

2021-12-10 09:48:24

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Fri, Dec 10, 2021 at 06:30:03AM -0300, Marcelo Henrique Cerri wrote:
> On Fri, Dec 10, 2021 at 07:46:24AM +0100, Greg Kroah-Hartman wrote:
> > On Thu, Dec 09, 2021 at 10:43:37PM -0300, Marcelo Henrique Cerri wrote:
> > > Hi, Jason. How do you think we could approach that then?
> > >
> > > Are you willing to discuss the FIPS 140-3 requirements that random.c
> > > doesn't currently meet so we can dive deeper on how we could implement
> > > them in a way that would improve the kernel other then simply
> > > providing compliance to FIPS?
> >
> > Discussing things doesn't usually work well. Let's see some working
> > patches first, that solve problems that you have with the current random
> > code, and we can go from there.
> >
> > Again, like any other kernel patch submission, nothing new here at all.
>
> Hi, Greg. I understand your point but we had plenty of patch
> submissions already from Stephan, Nicolai and others and that didn't
> work. So I am expecting that anybody taking over as the random.c
> maintainer can at least provide some direction on that.

Then submit patches to be reviewed! This patch series was commented on
why it is not acceptable, so it's done with for now.

We can't go back in time and dig up old patch series to be reviewed now
unless they are actually refreshed and resubmitted.

Why isn't anyone doing that?

> > > I believe all the distros are interested in making progress on that,
> > > but without a general guidance it makes very hard for us to
> > > collaborate and we end up in the current situation in which each
> > > distro is carrying its own "hack", as Simo mentioned before. Canonical
> > > is in the same situation as the other distros and we are carrying an
> > > workaround to wire up the crypto DRBG to random.c in order to archive
> > > compliance.
> >
> > If everyone seems to think their patches are hacks, and are not worthy
> > of being submitted, then why do they think that somehow they are viable
> > for their users that are actually using them?
>
> Because although some people dislike it, FIPS is still a requirement
> for many users. That's the reality and that will not change just
> because there are some resistance against it.
>
> The patches that distros are carrying are hacks because they try to
> minimize risks while keeping the code as close as possible to
> upstream. But that has several drawbacks, such as performance, limited
> entropy sources an so on, that to me makes them not suitable for
> upstream.

In other words, "the hacks we made to the random code are so bad we do
not want to submit them upstream for everyone to review as our names
would be on them and we would have to justify them to the world"? :)

Given that there are no patches here to review by anyone, why is this
email thread still persisting?

Again, the only way forward is to submit changes that meet our
well-documented development process. There's nothing "special" about
this very tiny .c file that is any different than the other 30 million
lines of kernel code we support that warrants a different process at
all.

greg k-h

2021-12-10 17:02:42

by Simo Sorce

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Fri, 2021-12-10 at 10:48 +0100, Greg Kroah-Hartman wrote:
> On Fri, Dec 10, 2021 at 06:30:03AM -0300, Marcelo Henrique Cerri wrote:
> > On Fri, Dec 10, 2021 at 07:46:24AM +0100, Greg Kroah-Hartman wrote:
> > > On Thu, Dec 09, 2021 at 10:43:37PM -0300, Marcelo Henrique Cerri wrote:
> > > > Hi, Jason. How do you think we could approach that then?
> > > >
> > > > Are you willing to discuss the FIPS 140-3 requirements that random.c
> > > > doesn't currently meet so we can dive deeper on how we could implement
> > > > them in a way that would improve the kernel other then simply
> > > > providing compliance to FIPS?
> > >
> > > Discussing things doesn't usually work well. Let's see some working
> > > patches first, that solve problems that you have with the current random
> > > code, and we can go from there.
> > >
> > > Again, like any other kernel patch submission, nothing new here at all.
> >
> > Hi, Greg. I understand your point but we had plenty of patch
> > submissions already from Stephan, Nicolai and others and that didn't
> > work. So I am expecting that anybody taking over as the random.c
> > maintainer can at least provide some direction on that.
>
> Then submit patches to be reviewed! This patch series was commented on
> why it is not acceptable, so it's done with for now.
>
> We can't go back in time and dig up old patch series to be reviewed now
> unless they are actually refreshed and resubmitted.
>
> Why isn't anyone doing that?

I think people would at least like to know they are not spending a
bunch of time and work to have another patch series go into the void,
or be rejected again, *after* all the hard work is done, without a
foreword of what is acceptable.

>
> > > > I believe all the distros are interested in making progress on that,
> > > > but without a general guidance it makes very hard for us to
> > > > collaborate and we end up in the current situation in which each
> > > > distro is carrying its own "hack", as Simo mentioned before. Canonical
> > > > is in the same situation as the other distros and we are carrying an
> > > > workaround to wire up the crypto DRBG to random.c in order to archive
> > > > compliance.
> > >
> > > If everyone seems to think their patches are hacks, and are not worthy
> > > of being submitted, then why do they think that somehow they are viable
> > > for their users that are actually using them?
> >
> > Because although some people dislike it, FIPS is still a requirement
> > for many users. That's the reality and that will not change just
> > because there are some resistance against it.
> >
> > The patches that distros are carrying are hacks because they try to
> > minimize risks while keeping the code as close as possible to
> > upstream. But that has several drawbacks, such as performance, limited
> > entropy sources an so on, that to me makes them not suitable for
> > upstream.
>
> In other words, "the hacks we made to the random code are so bad we do
> not want to submit them upstream for everyone to review as our names
> would be on them and we would have to justify them to the world"? :)

Our patches are all public in our respective tress, with names and all.
The hacks are not necessarily *bad*, but they are changes made because
we could not put in what we think would be a better solution. They can
definitely be sent upstream.

> Given that there are no patches here to review by anyone, why is this
> email thread still persisting?

There is a will and a need to "improve" things, but given past absence
of feedback, people are trying to understand if there is any point in
trying to submit patches. Patches are work, and people like to know
they are not wasting their time completely before committing many more
hours.

> Again, the only way forward is to submit changes that meet our
> well-documented development process. There's nothing "special" about
> this very tiny .c file that is any different than the other 30 million
> lines of kernel code we support that warrants a different process at
> all.

This very thread shows that there is an issue, people are not asking
for exceptions to the process, they are only asking for direction from
the maintainer so they can work productively and get some result, that
is all the "special" there is here.

Simo.

--
Simo Sorce
RHEL Crypto Team
Red Hat, Inc





2021-12-11 07:08:08

by Willy Tarreau

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Fri, Dec 10, 2021 at 12:02:35PM -0500, Simo Sorce wrote:
> On Fri, 2021-12-10 at 10:48 +0100, Greg Kroah-Hartman wrote:
> > Given that there are no patches here to review by anyone, why is this
> > email thread still persisting?
>
> There is a will and a need to "improve" things, but given past absence
> of feedback, people are trying to understand if there is any point in
> trying to submit patches. Patches are work, and people like to know
> they are not wasting their time completely before committing many more
> hours.

It is obviously natural to think this way, but you can also understand
that reviewing patches is extremely time consuming. And it's extremely
difficult to review a patch series which says "replace all that
infrastructure with a new one", especially when the motivations are
"comply with this or that standard" without the benefits being obvious
at all for those having to review those patches. And keep in mind that
those who you expect to review the code will have to maintain it, so if
the benefit is not obvious, why would they take the risk of breaking
something that's been working well enough or that has been easy enough
to improve or fix over time ?

My feeling from the beginning is that nobody felt brave enough to go
through these series because of this.

The normal way to propose changes is to say "some of our customers ask
for FIPS, we've looked at *what is missing* to accomplish that, first
it suggests/requires/mandates properties X, Y and Z which are currently
not supported, so these patches improve the current code by adding such
properties". And you don't patch "for FIPS", you patch to make the
existing code evolve to support such missing properties or features,
till the point you figure that nothing is missing anymore, and you can
tell your customers "now we comply with FIPS". And if it takes several
versions to reach that point, no problem, because each version continues
to work like before, possibly better, possibly not.

It's not different from supporting a new hardware. You don't bring in
a big patch series implementing all of the machine's device drivers in
an isolated area specific to that device. Instead you bring the various
parts this machine relies on (serial, pcie, usb, network etc), possibly
by improving existing drivers that are already very close or share some
common parts, and at the end you figure you have everything you need
and then you can proudly say "now we fully support this device".

This way of proceeding incrementally allows submitters not to waste
time coding for nothing, and those reviewing changes to make sure
they're not breaking everything, or to ask for some changes to stay
safe.

But this does mean that a list of incremental changes/additions has to
be established on the submitter's side, not a list of replacements.
Sometimes it is required to replace a part, but the justification has
to be technical, not "this part doesn't meet standard X or Y, let's
reinvent it". And such replacements need to be minimal so that it's
obvious they continue to provide exactly the same services and it's
almost impossible that a bisect lands on such a patch when something
stops working (i.e. if it happens it's just a coding bug and not a
design mistake).

> > Again, the only way forward is to submit changes that meet our
> > well-documented development process. There's nothing "special" about
> > this very tiny .c file that is any different than the other 30 million
> > lines of kernel code we support that warrants a different process at
> > all.
>
> This very thread shows that there is an issue, people are not asking
> for exceptions to the process, they are only asking for direction from
> the maintainer so they can work productively and get some result, that
> is all the "special" there is here.

At least it's visible in this very thread's subject that it's addressed
in a special way: "/dev/random - a new approach", i.e. "trash all what
we have and restart from scratch". This is exactly what is causing the
problem from the beginning in my opinion. But at this point I think
that Jason, Greg and others have already been saying it, so I'll stop.

Hoping this helps,

Willy

2021-12-11 08:11:17

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Am Samstag, 11. Dezember 2021, 08:06:10 CET schrieb Willy Tarreau:

Hi Willy,

> On Fri, Dec 10, 2021 at 12:02:35PM -0500, Simo Sorce wrote:
> > On Fri, 2021-12-10 at 10:48 +0100, Greg Kroah-Hartman wrote:
> > > Given that there are no patches here to review by anyone, why is this
> > > email thread still persisting?
> >
> > There is a will and a need to "improve" things, but given past absence
> > of feedback, people are trying to understand if there is any point in
> > trying to submit patches. Patches are work, and people like to know
> > they are not wasting their time completely before committing many more
> > hours.
>
> It is obviously natural to think this way, but you can also understand
> that reviewing patches is extremely time consuming. And it's extremely
> difficult to review a patch series which says "replace all that
> infrastructure with a new one", especially when the motivations are
> "comply with this or that standard" without the benefits being obvious
> at all for those having to review those patches.

I am so surprised by such statements. Patch 00/15 lists in a bullet list the
significant benefits of the LRNG. But seemingly nobody reads the introduction
with its concise bullet list or the documentation. The FIPS bits are a tiny
aspect of the whole effort (which even can be completely compiled out based on
config options), the more significant aspects that have nothing to do with
FIPS and benefit all are testability, performance, use of contemporary
cryptography, and flexibility.

> But this does mean that a list of incremental changes/additions has to
> be established on the submitter's side, not a list of replacements.

Before I started the endeavor of the stand-alone patch of the LRNG, I
developed cleanup patches to random.c in 2014 and 2015. I got massively
discouraged to continue working on random.c as I did not get feedback from the
maintainer. Some patches were taken, some were not without a comment...

Ciao
Stephan



2021-12-11 08:58:17

by Willy Tarreau

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Hi Stephan,

On Sat, Dec 11, 2021 at 09:09:55AM +0100, Stephan M?ller wrote:
> > It is obviously natural to think this way, but you can also understand
> > that reviewing patches is extremely time consuming. And it's extremely
> > difficult to review a patch series which says "replace all that
> > infrastructure with a new one", especially when the motivations are
> > "comply with this or that standard" without the benefits being obvious
> > at all for those having to review those patches.
>
> I am so surprised by such statements. Patch 00/15 lists in a bullet list the
> significant benefits of the LRNG. But seemingly nobody reads the introduction
> with its concise bullet list or the documentation. The FIPS bits are a tiny
> aspect of the whole effort (which even can be completely compiled out based on
> config options), the more significant aspects that have nothing to do with
> FIPS and benefit all are testability, performance, use of contemporary
> cryptography, and flexibility.

But this is where the problem is. You're not proposing to improve the
current one but to replace it. Who has enough energy to review some new
code that claims to be compatible with older one ? It requires to perform
a mental "diff" which is extremely complicated. There are possibly corner
cases in the current code that nobody knows about and that some code
currently relies on. Who wlil detect that you're not going to break them
with a fresh new implementation ?

Incremental changes allow to focus on the changes. You don't need to
know how everything else works, just that the modifications do not
break the part they are inserted into. This makes a huge difference,
and this is why everyone constantly insists on seeing small incremental
changes. Sometimes you'll notice that you can even see review from
different people for very close parts in the same file.

> > But this does mean that a list of incremental changes/additions has to
> > be established on the submitter's side, not a list of replacements.
>
> Before I started the endeavor of the stand-alone patch of the LRNG, I
> developed cleanup patches to random.c in 2014 and 2015. I got massively
> discouraged to continue working on random.c as I did not get feedback from the
> maintainer. Some patches were taken, some were not without a comment...

We all know that this is extremely irritating. It happens everywhere and
in every project. Sometimes lack of time, lost messages, flipped priorities,
or lack of interest, or a combination of all of this. I do it myself from
time to time by accident and I really feel bad when this happens. That's
not much different than when you're reminding a coworker that you need
their help and they forget because of other priorities. And this must
never discourage one from pinging again and asking why there's no
response. But one thing is certain to me, it's that if a maintainer,
for any reason, doesn't respond to tiny patches to their code, there's
hardly any chance to see a response to a whole replacement.

Here Jason offered to invest time reviewing changes. If you have changes
to propose, why not try ? And even if it takes one year to get everything
done, who cares ? You seem to have been working on this for 7 years
already, it might be worth trying another approach.

Regards,
Willy

2021-12-11 15:58:04

by Thomas Schoebel-Theuer

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

On 11/21/21 5:39 PM, Stephan Müller wrote:
> The following patch set provides a complete production-
> ready and yet 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.

My suggestion: please name it /dev/fastrandom or /dev/fips-random or
/dev/scalable-random or whatever is appropriate, and please leave the
traditional /dev/random as it was for decades.

My reasons:

I am one of the downstream kernel responsibles you might affect with
your changes. I am responsible for any kernel issues for millions of
customers.

For me, the _slowness_ of the traditional /dev/random is a _feature_.

Some more detailed arguments, important for my use cases as seen by me:

1) Personally, I have made some 1&1-internal scripts which don't rely on
a single source of entropy, as seen by sysadmins. Note that each
/dev/$other_random is a _single_ _source_ of entropy from a sysadmin's
perspective (and also a "blackbox"), although the role is different from
a kernel developer's perspective.

2) Any new kernel must be able to run on any of our machines, even very
old machines (bound by old customer contracts) which don't have certain
hardware features, or have a different non-functional behaviour like
performance, or interrupt timing behaviour, etc. Thus the non-functional
behaviour of the traditional /dev/random is important for me. Please do
not change it.

3) Whenever a new kernel behaviour is "discovered" by some userspace
developers (whether in-house or from many OpenSource projects around the
world), they tend to use it sooner or later, for whatever reason. If
suchalike would happen at the traditional /dev/random, it would be
noticed by some of our sysadmin teams. Here is the reason:

4) Collection of entropy vs consumption of entropy: the old /dev/random
has an important feature for me: any _mass_ usage by whatever class of
users (whether tenthousands of UIDs per server and/or HTTP/second, or
maybe even some privileged orchestration scripts) would _consume_ masses
of entropy. When suchalike consumption would exceed the production rate,
the old /dev/random would become so slow that our internal monitoring
processes would certainly alert, and consequently would hint our
responsibles (located at other teams) at the problem. Traditionally,
/dev/random and /dev/urandom are thus used for different purposes.

5) Please don't misunderstand me: I am _not_ against the bazaar model
which allows you to develop new and interesting features. Just don't
throw away the traditional solutions, encapsulating huge amounts of
manpower. Please create a new booth at the bazaar. Then some hundreds of
userspace developers can support the new solution, or even  migrate from
traditional interfaces like /dev/urandom to newer ones. Many userspace
projects are widely distributed, and independent from each other. By
providing a different interface (which is easily detectable), separation
of concerns will become easy in a worldwide scale.

Hints: whenever changing / improving non-functional properties of your
new /dev/$new_random, please report _separate_ version numbers for
non-functional vs feature versions. Please maintain it over many years
(hopefully comparable to the lifetime of /dev/random). Please long-term
document the rules how its new features should be _interpreted_. Please
document important use cases. Please create a better documentation than
the traditional ones, also understandable by sysadmins (not only by
certain developers or security experts). Even more important: please
document and depict _scenarios_ where certain features should _NOT_ be used.

Cheers and very sincerly,

Thomas

Homepage: https://github.com/schoebel and look into the mars/ project
and also into its docu/ subfolder. Please read the big pdfs. Then you
might notice that I could become a potential future user of your new code.


2021-12-11 16:05:47

by Willy Tarreau

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

On Sat, Dec 11, 2021 at 04:45:55PM +0100, Thomas Schoebel-Theuer wrote:
> 4) Collection of entropy vs consumption of entropy: the old /dev/random has
> an important feature for me: any _mass_ usage by whatever class of users
> (whether tenthousands of UIDs per server and/or HTTP/second, or maybe even
> some privileged orchestration scripts) would _consume_ masses of entropy.
> When suchalike consumption would exceed the production rate, the old
> /dev/random would become so slow that our internal monitoring processes
> would certainly alert, and consequently would hint our responsibles (located
> at other teams) at the problem.

I'm sorry but I cannot agree with you on this. You are claiming that your
monitoring processes are so limited that the only situation they can
discover is when the machine is basically dead. There are plenty of
users who end up replacing /dev/random with /dev/urandom in production
to make sure a terrible service outage never happens again, and one
important feature of an RNG is its performance, particularly when it's
shared between processes and users. The fact that your monitoring only
triggers when the system becomes unusable is a proof that it must be
fixed, and certainly not an indication that any possible kernel
limitation you're benefitting from does not deserve to be addressed.

Willy

2022-01-10 13:24:12

by Marcelo Henrique Cerri

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Thu, Dec 09, 2021 at 10:43:37PM -0300, Marcelo Henrique Cerri wrote:
> On Wed, Dec 01, 2021 at 11:02:38AM -0500, Jason A. Donenfeld wrote:
> > Hi Simo,
> >
> > I think various folks have said this during the various discussions on this
> > topic over the years, in addition to myself, but I suppose I'll reiterate my
> > general views on FIPS in this context.
> >
> > FIPS is about compliance and certification. From a cryptographic point of
> > view, there might be some good ideas, some dated ideas, some superfluous but
> > harmless ideas, and so forth. But the reason that you want it for your
> > customers is because you think your product will become more valuable or
> > useful to customers if it checks that green compliance checkbox. I don't think
> > we disagree about this being the motivation.
> >
> > Now typically the kernel interoperates with lots of things and implements many
> > different specifications. It supports scores of network protocols, IPsec
> > cipher suites, USB quirks, SCSI hacks, you name it. The implementation of
> > these drivers is always up to the author and hopefully kernel developers at
> > large do the best job they can with the implementation, but the hardware or
> > protocol they're interfacing with is not up to the author, by virtue of it
> > being external to the kernel. It's not like instantiating IPsec with single
> > DES and MD4, or SM3 and SM4, etc. is so great, and it's not like the
> > compendium of brilliant hacks in drivers/usb/host/pci-quirks.c is so great
> > either. But these things all exist to talk to something *outside* of the
> > kernel, and so we grit our teeth, and as I said, do the best we can to
> > implement it well.
> >
> > But the RNG isn't like that. In fact, the RNG is logically *required* to be
> > not anything like that: it returns random bytes, and they must not have any
> > distinguishing quality with other random bytes; otherwise we have a serious
> > problem that needs fixing. And so, we carry things out according to the usual
> > kernel developer mindset: we implement it as best as we can, using the best
> > algorithms we can find, in a way most suitable for the kernel.
> >
> > Then FIPS comes along and starts dictating things about *how* we implement it,
> > and those things it dictates might not be exactly the same as what we would
> > would be doing when doing best that we can, using the best algorithms we can
> > find, and in the most suitable way for the kernel. And so it would seem that
> > the goal of implementing the RNG as best as we can might potentially be at
> > odds with the goal of getting that green compliance checkbox, because that
> > checkbox oversteps its bounds a bit.
> >
> > That's not to say, of course, that we shouldn't accept input on how we
> > implement our algorithms from elsewhere. On the contrary, I think random.c has
> > a *lot* to gain from incorporating newer ideas, and that the formalism and
> > guidance from academic cryptographers is less "academic" than it once was and
> > much more real world, implementable, and suitable for our uses. But, again,
> > incorporating new ideas and accepting input on how to improve our code is very
> > much not the same thing as following the FIPS laundry list for that green
> > compliance checkbox. Maybe some parts do overlap -- and I'd love patches that
> > improve the code alongside compelling cryptographic arguments -- but, again,
> > we're talking about compliance here, and not a more welcome, "hey check out
> > this document I found with a bunch of great ideas we should implement."
> >
> > I would like the kernel to have an excellent CSPRNG, from a cryptographic
> > point of view, from a performance point of view, from an API point of view. I
> > think these motivations are consistent with how the kernel is generally
> > developed. And I think front loading the motivations with an external
> > compliance goal greatly deviates and even detracts from the way the kernel is
> > generally developed.
> >
> > Now the above is somewhat negative on FIPS, but the question can still be
> > posed: does FIPS have a path forward in the RNG in the kernel? It's obviously
> > not a resounding "yes", but I don't think it's a totally certain "no" either.
> > It might be possible to find some wiggle room. I'm not saying that it is
> > certainly possible to do that, but it might be.
> >
> > Specifically, I think that if you change your perspective from, "how can we
> > change the algorithms of the RNG to be FIPS" to "how can we bend FIPS within
> > its limits so that having what customers want would minimally impact the
> > quality of the RNG implementation or introduce undue maintenance burdens."
> > This means: not refactoring the RNG into some large abstraction layer that's
> > pluggable and supports multiple different implementations, not rewriting the
> > world in a massive patchset, not adding clutter. Instead, perhaps there's a
> > very, very minimal set of things that can be done that would be considerably
> > less controversial. That will probably require from you and other FIPS
> > enthusiasts some study and discussion at what the truly most minimal set of
> > things required are to get you that green compliance checkbox. And hey --
> > maybe it's still way too much and it doesn't work out here. But maybe it's not
> > that much, or, as Greg suggested, maybe it winds up that your needs are
> > actually satisfied just fine by something in userspace or userspace-adjacent.
> >
> > So I don't know whether the FIPS has a path forward here, but if it does, I
> > think the above is the general shape it would take. And in the mean time, I'm
> > of course open to reviewing patches that improve the RNG in a cryptographic or
> > algorithmic sense, rather than a purely compliance one.
>
> Hi, Jason. How do you think we could approach that then?
>
> Are you willing to discuss the FIPS 140-3 requirements that random.c
> doesn't currently meet so we can dive deeper on how we could implement
> them in a way that would improve the kernel other then simply
> providing compliance to FIPS?
>
> I believe that several requirements would be beneficial to random.c
> (ie, health test, oversampling, entropy data collection). But so far
> we lack proper direction on how to proceed and it would be better for
> us to have a clear notion of what could be accepted before putting
> more effort on yet another patch set.
>
> I believe all the distros are interested in making progress on that,
> but without a general guidance it makes very hard for us to
> collaborate and we end up in the current situation in which each
> distro is carrying its own "hack", as Simo mentioned before. Canonical
> is in the same situation as the other distros and we are carrying an
> workaround to wire up the crypto DRBG to random.c in order to archive
> compliance.
>

Hoping that might help with the discussion and to explain why I do
consider those solutions a "hack", that's the patch we've been using
so far to achieve SP 800-90B compliance:

https://kernel.ubuntu.com/~mhcerri/0001-UBUNTU-SAUCE-random-Use-Crypto-API-DRBG-for-urandom-.patch


> We could also concentrate all the discussion in the linux-crypto
> mailing list to facilitate this process, since right now I believe the
> MAINTAINERS file doesn't have a specific mailing list associate to
> random.c
>
> >
> > Hopefully that helps you understand more about where we're coming from.
> >
> > Regards,
> > Jason
>
> --
> Regards,
> Marcelo
>



--
Regards,
Marcelo


Attachments:
(No filename) (7.37 kB)
signature.asc (659.00 B)
Download all attachments

2022-01-10 14:12:08

by Jason A. Donenfeld

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Hi Marcelo,

On Mon, Jan 10, 2022 at 2:24 PM Marcelo Henrique Cerri
<[email protected]> wrote:
> Hoping that might help with the discussion and to explain why I do
> consider those solutions a "hack", that's the patch we've been using
> so far to achieve SP 800-90B compliance:
>
> https://kernel.ubuntu.com/~mhcerri/0001-UBUNTU-SAUCE-random-Use-Crypto-API-DRBG-for-urandom-.patch

Thanks for sending this in response to my request for it in our private thread.

Just to confirm, this little patch here gives you FIPS certification?

Jason

2022-01-10 14:31:18

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Jan 10, 2022 at 03:11:46PM +0100, Jason A. Donenfeld wrote:
> On Mon, Jan 10, 2022 at 2:24 PM Marcelo Henrique Cerri
> <[email protected]> wrote:
> > Hoping that might help with the discussion and to explain why I do
> > consider those solutions a "hack", that's the patch we've been using
> > so far to achieve SP 800-90B compliance:
> >
> > https://kernel.ubuntu.com/~mhcerri/0001-UBUNTU-SAUCE-random-Use-Crypto-API-DRBG-for-urandom-.patch
>
> Thanks for sending this in response to my request for it in our private thread.
>
> Just to confirm, this little patch here gives you FIPS certification?

There might be some FIPS certification labs that might be willing to
be taken in by the jitterentropy story, but when I've had private
communications from people who are familiar with the Intel
microarchitecture saying that jitterentropy is mostly "security by
obscurity", I'd be strongly opposed to replacing the current scheme
with something which is purely jitteretropy.

Perhaps an build-time option where one of the seeds into the CRNG is
"jitterentropy", but we keep everything else. That way, jitterentropy
can still be TSA-style "security theatre", but we're not utterly
dependant on the "the CPU microarchitecture is SOOOOOOO complicated,
it *must* be unpredictable".

- Ted

2022-01-10 14:38:29

by Jason A. Donenfeld

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Hi Ted,

On Mon, Jan 10, 2022 at 3:31 PM Theodore Ts'o <[email protected]> wrote:
> There might be some FIPS certification labs that might be willing to
> be taken in by the jitterentropy story, but when I've had private
> communications from people who are familiar with the Intel
> microarchitecture saying that jitterentropy is mostly "security by
> obscurity", I'd be strongly opposed to replacing the current scheme
> with something which is purely jitteretropy.
>
> Perhaps an build-time option where one of the seeds into the CRNG is
> "jitterentropy", but we keep everything else. That way, jitterentropy
> can still be TSA-style "security theatre", but we're not utterly
> dependant on the "the CPU microarchitecture is SOOOOOOO complicated,
> it *must* be unpredictable".

Yea, I'm not really compelled by it as something real that we'd
actually want to have for something serious. Keep in mind: this thread
isn't really about cryptography, but just about compliance nonsense.
BUT, if it turns out that the path to these people getting their green
compliance checkbox stamp isn't actually thousands of lines of new
code, but rather some glue bridging the /dev/urandom / getrandom(2)
API into the blah cryptoapi thing, that's... interesting news to me.
I'm not even saying, at this stage anyhow, that I want to do this, but
I do find it a very interesting data point. As I wrote in [1]:

> Specifically, I think that if you change your perspective from, "how can we
> change the algorithms of the RNG to be FIPS" to "how can we bend FIPS within
> its limits so that having what customers want would minimally impact the
> quality of the RNG implementation or introduce undue maintenance burdens."

We're now starting to get some idea about how this FIPS stuff bends.

Jason

[1] https://lore.kernel.org/lkml/CAHmME9qP9eYfPH+8eRvpx_tW8iAtDc-byVMvh4tFL_cABdsiOA@mail.gmail.com/

2022-01-10 15:08:18

by Marcelo Henrique Cerri

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Jan 10, 2022 at 09:29:04AM -0500, Theodore Ts'o wrote:
> On Mon, Jan 10, 2022 at 03:11:46PM +0100, Jason A. Donenfeld wrote:
> > On Mon, Jan 10, 2022 at 2:24 PM Marcelo Henrique Cerri
> > <[email protected]> wrote:
> > > Hoping that might help with the discussion and to explain why I do
> > > consider those solutions a "hack", that's the patch we've been using
> > > so far to achieve SP 800-90B compliance:
> > >
> > > https://kernel.ubuntu.com/~mhcerri/0001-UBUNTU-SAUCE-random-Use-Crypto-API-DRBG-for-urandom-.patch
> >
> > Thanks for sending this in response to my request for it in our private thread.

No problem. And sorry for the delay.

> >
> > Just to confirm, this little patch here gives you FIPS certification?

It does because it basically replaces everything in random.c (for
urandom in this case) with the Crypto API DRBG, which is
compliant. Although it might be wiser to replace both urandom and
random in this case.

>
> There might be some FIPS certification labs that might be willing to
> be taken in by the jitterentropy story, but when I've had private
> communications from people who are familiar with the Intel
> microarchitecture saying that jitterentropy is mostly "security by
> obscurity", I'd be strongly opposed to replacing the current scheme
> with something which is purely jitteretropy.
>
> Perhaps an build-time option where one of the seeds into the CRNG is
> "jitterentropy", but we keep everything else. That way, jitterentropy
> can still be TSA-style "security theatre", but we're not utterly
> dependant on the "the CPU microarchitecture is SOOOOOOO complicated,
> it *must* be unpredictable".
>

Hi, Theodore.

I might be missing something, but the Crypto API DRBG is seeded by
jitterentropy_rng and by get_random_bytes(), their outputs are both
concatenated and used as the seed. So I don't think that should be a
concern, right?


> - Ted

--
Regards,
Marcelo


Attachments:
(No filename) (1.89 kB)
signature.asc (659.00 B)
Download all attachments

2022-01-10 17:39:54

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Jan 10, 2022 at 03:38:08PM +0100, Jason A. Donenfeld wrote:
>
> Yea, I'm not really compelled by it as something real that we'd
> actually want to have for something serious. Keep in mind: this thread
> isn't really about cryptography, but just about compliance nonsense.
> BUT, if it turns out that the path to these people getting their green
> compliance checkbox stamp isn't actually thousands of lines of new
> code, but rather some glue bridging the /dev/urandom / getrandom(2)
> API into the blah cryptoapi thing, that's... interesting news to me.
> I'm not even saying, at this stage anyhow, that I want to do this, but
> I do find it a very interesting data point.

The last time I had the displeasure of looking into the FIPS
certification, which granted, was over a decade ago when I was in
IBM's Linux Technology Center, what I learned was it all depends on
the FIPS certification lab. NIST writes the documentation, but what
really matters is the FIPS certification lab that a hardware or
software vendor pays $$$$ to in order to get the magic certificate
which allows you to sell into *some* government contracts. (When I
had to go through the all of the nonsense to get a TS/SCI clearance to
support the real-time kernel for all of the IBM servers for the
DDG/1000 Zumwalt Class destroyer, they didn't care about getting FIPS
certified. I've also seen tighter security measures for computer
rooms at NYC financial companies than at a Top Secret machine root at
said defense contractor. Go figure....)

The other thing I learned for those customers who *did* care, was that
the only thing that got certified was a specific binary image. If you
replace the kernel or OpenSSL library with, say, a bugfixed version
that fixed an actively exploited zero day, *boom*, that would break
the certification and the system would no longer be FIPS certified.

Some FIPS labs would allow you to certify the "cryptographic core" of
the OpenSSL library, which the OpenSSL library would then dlopen, and
so as long as the bugfix was in, say, the ASN.1 parser, and you didn't
need to change the "cryptographic core" it was OK --- and you just
needed to hope that there weren't any bugs in the cryptographic core,
since then you wouldn't be allowed to fix it --- since FIPS compliance
was more important than, say, the *actual* security of the system in
question.

So yeah, as I said, it's all about TSA-style security theatre, and
when I worked for IBM and there was millions of dollars on the line, I
might have cared. For upstream development, (and blessedly, this is
not something that my current employer has needed to worry about) I
care far less about it.

If we want to add a CONFIG_RANDOM_SECURITY_THEATRE build option which
diverts getrandom and /dev/urandom to use crypto/drbg, I'm going to
think it's a waste of time, and there are some things about
crypto/drbg that I'm not psyched about such as the fact that only
reseed after 2**20 calls to drbg_generate(), and the drbg statemachine
will initialize itself from get_random_bytes() in early boot, when the
CRNG is least likely to be securely initialized. So **I** wouldn't
want to use it for my own personal security, but if it allows Ubuntu
to sell into the US govnerment market, my only hope is that this
wouldn't be inflicted on all of their customers, but only those US
Government customers who care (and as near as I can tell, this is
*not* all USG customers).

> > Specifically, I think that if you change your perspective from, "how can we
> > change the algorithms of the RNG to be FIPS" to "how can we bend FIPS within
> > its limits so that having what customers want would minimally impact the
> > quality of the RNG implementation or introduce undue maintenance burdens."
>
> We're now starting to get some idea about how this FIPS stuff bends.

Well, if we optionally (if jitterentropy_rng is compiled in), we would
periodically pull from it as one additional entropy source into the
input_pool, it won't do any harm --- other than the CPU overhead
consumed by jitterentropy, of course. Maybe that would make some
people happy, including some FIPS Labs?

I've also seen some FIPS certifications which didn't care about what
the kernel did, but only cared about what was in the OpenSSL library.
(Which is where the story about segregating out the cryptographic core
so that you could actually patch most zero-days without having to go
back to the FIPS certification lab, pay $$$, and wait months for the
updated binary to be certified.)

So I suspect that there will be a lot of anecdotal evidence, but the
only thing we can probably say with any amount of certainity is Your
Mileage May Vary.

Cheers,

- Ted

2022-01-10 18:29:45

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Jan 10, 2022 at 12:38:00PM -0500, Theodore Ts'o wrote:
> If we want to add a CONFIG_RANDOM_SECURITY_THEATRE build option which
> diverts getrandom and /dev/urandom to use crypto/drbg, I'm going to
> think it's a waste of time, and there are some things about
> crypto/drbg that I'm not psyched about such as the fact that only
> reseed after 2**20 calls to drbg_generate(), and the drbg statemachine
> will initialize itself from get_random_bytes() in early boot, when the
> CRNG is least likely to be securely initialized. So **I** wouldn't
> want to use it for my own personal security, but if it allows Ubuntu
> to sell into the US govnerment market, my only hope is that this
> wouldn't be inflicted on all of their customers, but only those US
> Government customers who care (and as near as I can tell, this is
> *not* all USG customers).
>

So just a few thoughts:

Ubuntu, Red Hat, and Oracle all have patches which do this. They differ
slightly; e.g., Ubuntu's patch only changes /dev/urandom while the others change
/dev/random and getrandom() too. But the idea is the same: the userspace
interfaces to the RNG are changed to get output from a SP800-90A DRBG
(crypto/drbg.c) rather than the Linux RNG directly. The SP800-90A DRBG in turn
is seeded from from two entropy sources combined: the Linux RNG
(get_random_bytes()) and jitterentropy (crypto/jitterentropy.c).

My understanding (and I could be totally wrong -- I am still trying to reverse
engineer all the requirements for this certification stuff) is that the reason
that these distros need this is they are certifying the whole kernel image as a
FIPS cryptographic module, and that implies that cryptographic random numbers
must conform to the SP800-90{A-C} documents. The problem is that ChaCha20 isn't
considered an approved DRBG algorithm, nor do Linux's entropy sources have
SP800-90B continuous health-tests. Therefore, get_random_bytes() is considered
to provide no entropy. crypto/drbg.c works around this by using an approved
DRBG algorithm and by using jitterentropy which has SP800-90B tests.

I think the reason people are considering this to be a hack is because on paper
it ignores Linux's main RNG. It's still *used* as an extra entropy input, but
on paper it's credited with no entropy. That seems a bit odd.

However, even Stephan's patchset has the same issues, IIUC. Stephan's patchset
still keeps get_random_bytes() using ChaCha20, and it provides an option to
layer crypto/drbg.c on top of it for userspace output. So I'm not sure how much
of a hack it really is, if the supposed non-hack is basically the same.

Now, the idea of certifying the whole kernel as a FIPS cryptographic module is
stupid, given that it prevents the kernel from being updated to fix security
vulnerabilities. However, I've been told that essentially the same RNG issues
also arise for NIAP certification of mobile devices
(https://www.niap-ccevs.org/MMO/PP/PP_MDF_V3.2.pdf), which looks at entropy
system-wide. NIAP similarly doesn't consider ChaCha20 to be an allowed DRBG
algorithm, so they consider the entropy to be constantly depleting, and it "runs
out". (There have been devices that passed NIAP despite this, but I've been
told that this was an oversight.) Wiring up /dev/{u,}random and getrandom() to
crypto/drbg.c would avoid this issue too.

So again, I could be totally wrong, as I am trying to reverse engineer the
requirements here --- but to me it seems that a small patch to provide an option
to use crypto/drbg.c could solve both the FIPS and NIAP certification problems.

If Stephan could elaborate on what his patchset does that is better (as far as
certification is concerned, at least -- I know his patchset has some other
advantages such as eliminating non-cryptographic entropy processing), that would
be helpful to illuminate anything I may be missing.

- Eric

2022-01-10 18:44:53

by Jason A. Donenfeld

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Jan 10, 2022 at 4:08 PM Marcelo Henrique Cerri
<[email protected]> wrote:
> > Just to confirm, this little patch here gives you FIPS certification?
> It does

On Mon, Jan 10, 2022 at 7:29 PM Eric Biggers <[email protected]> wrote:
> Now, the idea of certifying the whole kernel as a FIPS cryptographic module is
> stupid

Alright, so if that's the case, then what you ostensibly want is:
a) Some cryptoapi users to use crypto_rng_get_bytes, as they already
do now. (In a private thread with Simo, I pointed out a missing place
and encouraged him to send a patch for that but none has arrived.)
b) Userspace to use some other RNG.

(a) is basically already done.

(b) can be accomplished in userspace by just (i) disabling getrandom()
(making it return ENOSYS), and then (ii) replacing the /dev/urandom
path with a CUSE device or similar.

I suppose (b.i) might be able to be done with some bpf seccomp cgroup
situation. Or, if that's problematic, somebody could propose a
"disable getrandom(2)" cmdline option. That doesn't seem very hard.
And (b.ii) could use combined inputs from /dev/urandom and whatever
FIPSy userspace jitter entropy daemon you have.

In order to prevent the actual security from regressing on this, all
you have to do is ensure that you're always using at least 32 bytes
from the kernel's real /dev/urandom, and then whatever you add on top
of that becomes just for the certification aspect. As your various
green compliance checkboxes change over time and per region, you can
just swap out the extra-paper-pushing-bytes-on-top with whatever the
particular requirements of a certification body are. And you get to do
this all in userspace.

Marcelo/Simo - could you tell me what you find deficient about that
plan? It strikes me that this would give you maximum flexibility and
pretty much accomplish the goals?

Thanks,
Jason

2022-01-10 19:41:45

by Simo Sorce

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, 2022-01-10 at 19:44 +0100, Jason A. Donenfeld wrote:
> On Mon, Jan 10, 2022 at 4:08 PM Marcelo Henrique Cerri
> <[email protected]> wrote:
> > > Just to confirm, this little patch here gives you FIPS certification?
> > It does
>
> On Mon, Jan 10, 2022 at 7:29 PM Eric Biggers <[email protected]> wrote:
> > Now, the idea of certifying the whole kernel as a FIPS cryptographic module is
> > stupid

Not that it is not the whole kernel, but a "module boundary" is drawn
around the crypto API and vicinity.
It would be really nice if this whole "boundary" could be built as a
single binary module to be loaded in the kernel in fips mode. That way
we could update the rest of the kernel w/o rebuilding the module, but
we are not there.

Rebuilding the kernel does technically invalidate certification however
NIST themselves tells people to care first about the security of the
systems as long as the vendor is undergoing or promising certification
of the patched kernel.

There is an assumption of good faith.

> Alright, so if that's the case, then what you ostensibly want is:
> a) Some cryptoapi users to use crypto_rng_get_bytes, as they already
> do now. (In a private thread with Simo, I pointed out a missing place
> and encouraged him to send a patch for that but none has arrived.)

I noted your point, just haven' had time to act on it.

> b) Userspace to use some other RNG.
>
> (a) is basically already done.
>
> (b) can be accomplished in userspace by just (i) disabling getrandom()
> (making it return ENOSYS), and then (ii) replacing the /dev/urandom
> path with a CUSE device or similar.

While this is technically possible it is not very helpful, as it
requires downstream patching of userspace programs, most of which do
not have either runtime nor compile time switches to change the used
random device.

> I suppose (b.i) might be able to be done with some bpf seccomp cgroup
> situation. Or, if that's problematic, somebody could propose a
> "disable getrandom(2)" cmdline option. That doesn't seem very hard.
> And (b.ii) could use combined inputs from /dev/urandom and whatever
> FIPSy userspace jitter entropy daemon you have.

It is simply easier to just patch /dev/[u]random/getrandom() to use a
certified DRBG in FIPS mode, although we also considered all the
options you mentioned we couldn't really find a good reason to add
more work, and make a more complicated solution when it is simple to
wire up the correct DRBG to the random device userspace applications
use and is the de facto standard API for obtaining good random numbers.

> In order to prevent the actual security from regressing on this, all
> you have to do is ensure that you're always using at least 32 bytes
> from the kernel's real /dev/urandom, and then whatever you add on top
> of that becomes just for the certification aspect. As your various
> green compliance checkboxes change over time and per region, you can
> just swap out the extra-paper-pushing-bytes-on-top with whatever the
> particular requirements of a certification body are. And you get to do
> this all in userspace.

You can do the whole jitterbug in userspace, but that is simply not
efficient and too disruptive (the above patching of all downstream
usage).

>
> Marcelo/Simo - could you tell me what you find deficient about that
> plan? It strikes me that this would give you maximum flexibility and
> pretty much accomplish the goals?

My goal is to deviate as little as possible both in kernel and user-
space from what upstreams do. Creating new interfaces is easy, making
people use them is almost impossible. Witness the process in getting
people to use getrandom()


Let me also add that NIST requirements are not capricious, they are
written by people that study entropy sources and random generation as
their job and know what they are doing, I err on the side of giving
them credit. The requirements set by the various 90A/90B/90C documents
are about raising the bar, to guarantee that random number generators
are actually "certifiably" good. There are entropy assessment performed
by the labs as part of the certification process to insure the random
source is a good source and does produce output that passes randomness
tests. I personally think the kernel would benefit from implementing
those "checkboxes", it is basically like implementing a sane CI/CD and
testing environment.

To answer to Ted,
every certification program necessarily requires a certain amount of
bureaucracy, especially when governments are involved, but that doesn't
mean that it's all security theater.

The FIPS certification process has changed over the years as well not
just the requirements. Until a few years ago the requirement to use
FIPS certified cryptography could be waived, and because very few
consumer programs were certified a lot of agencies considered it just a
burden and didn't care much. That has changed, it is now required as a
matter of law for most government agencies, and the waiver process has
been discontinued. So we really need to provide FIPS certification to
our public sector customers, moreover various other security standards
now reference FIPS standards, so it is extending beyond government
agencies and their contractors.

FIPS is painful for us undergoing certification, but as a program it
also does have positive effects. We scrutinize all cryptographic
modules a lot more than we used to, we have a lot more testing than we
used to and a lot more confidence in the solidity of the provided
cryptography in the Linux world also thanks to this scrutiny. I wish
the certification process was less painful for sure, but I believe it
does add value when done sensibly.

Simo.

--
Simo Sorce
RHEL Crypto Team
Red Hat, Inc





2022-01-10 19:51:20

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Jan 10, 2022 at 07:44:23PM +0100, Jason A. Donenfeld wrote:
> b) Userspace to use some other RNG.
>
> (b) can be accomplished in userspace by just (i) disabling getrandom()
> (making it return ENOSYS), and then (ii) replacing the /dev/urandom
> path with a CUSE device or similar.

I don't think you even need to do this. In general, you need FIPS
certification for some specific use cases / application. For example,
if you're going for PCI compliance, then you might only need FIPS
compliance for your OpenSSL library. What the FIPS certification lab
might consider acceptable for its entropy for its DRBG is an
interesting question. For some, simply having the OpenSSL library use
RDSEED or RDRAND might be sufficient. Or it could talk to an actual
physical RNG device.

So disabling getrandom() is probably not necessary, just so long as
you can demonstrate that the FIPS cryptographic module --- i.e., the
OpenSSL library --- is getting its entropy from an acceptable source.

I suspect what's actually going on is that some enterprise customers
have FIPS complaince on a check-off list, and they aren't actually
getting a formal FIPS certification. Or they only need something to
wave under the noses of their PCI certification company, and so the
question is what makes them happy.

Going into the details of whether ChaCha20 is blessed by FIPS is
probably more into technical weeds than most of the people who *say*
they want FIPS certification actually will go. After all, the
in-kernel DRBG is using as its "entropy source" the timing
instructions from a bunch of x86 assembly instructions which is
**soooo** complicated that people are willing to drink from the snake
oil and claim that it is secure. Is it really? Has FIPS said that
it's OK? Not any more than they've said anything about ChaCha20!

And this is why some FIPS certification have gotten by just *fine*
with a pure userspace OpenSSL library as their FIPS cryptographic
module. Where you draw the line between a "blessed" entropy source
and one that's just hand-waving is really at the discretion of the
certification lab.

Personally, if I was doing something that I really, *really* wanted to
be secure, I'd be mixing in several hardware RNG's. Given that most
server and client platforms have a TPM, or some other hardened
security module, using that is probably the best bet of I was
architecting some that *really* needed to be secure. But of course,
we're not talking about real security in this thread; we're talking
about whatever security theater will make the FIPS certification labs,
and the people who say they want FIPS on their check-off list, happy. :-)

- Ted

2022-01-10 20:05:16

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Jan 10, 2022 at 02:41:33PM -0500, Simo Sorce wrote:
> On Mon, 2022-01-10 at 19:44 +0100, Jason A. Donenfeld wrote:
> > On Mon, Jan 10, 2022 at 4:08 PM Marcelo Henrique Cerri
> > <[email protected]> wrote:
> > > > Just to confirm, this little patch here gives you FIPS certification?
> > > It does
> >
> > On Mon, Jan 10, 2022 at 7:29 PM Eric Biggers <[email protected]> wrote:
> > > Now, the idea of certifying the whole kernel as a FIPS cryptographic module is
> > > stupid
>
> Not that it is not the whole kernel, but a "module boundary" is drawn
> around the crypto API and vicinity.
> It would be really nice if this whole "boundary" could be built as a
> single binary module to be loaded in the kernel in fips mode. That way
> we could update the rest of the kernel w/o rebuilding the module, but
> we are not there.

FWIW, the "FIPS module as a loadable kernel module" approach was implemented in
the Android kernel; grep for "fips140" in branch "android13-5.10" of
https://android.googlesource.com/kernel/common. It's a lot of work for nothing
IMO, but the FIPS certification lab being used is happy with the approach.
Note that random.c is outside of the FIPS module with this approach.

- Eric

2022-01-10 21:38:52

by Jason A. Donenfeld

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Just in case you were curious...

On Mon, Jan 10, 2022 at 07:44:23PM +0100, Jason A. Donenfeld wrote:
> (b) can be accomplished in userspace by just (i) disabling getrandom()
> (making it return ENOSYS), and then (ii) replacing the /dev/urandom
> path with a CUSE device or similar.
>
> I suppose (b.i) might be able to be done with some bpf seccomp cgroup
> situation. Or, if that's problematic, somebody could propose a
> "disable getrandom(2)" cmdline option. That doesn't seem very hard.
> And (b.ii) could use combined inputs from /dev/urandom and whatever
> FIPSy userspace jitter entropy daemon you have.

The below took all of 5 minutes to write. Should be easy to tweak this
for whatever flavors required.

====

/* Copyright (C) 2022 Jason A. Donenfeld <[email protected]>. All Rights Reserved.
*
* Usage:
* # gcc -O2 jrandom.c `pkg-config fuse3 --cflags --libs` -o jrandom
* # ./jrandom
* # chmod 666 /dev/jrandom
* # ln -sf jrandom /dev/urandom
* # ln -sf jrandom /dev/random
*/


#define FUSE_USE_VERSION 31

#include <cuse_lowlevel.h>
#include <fuse_opt.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/if_alg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

static int rng;

static void fipsrng_open(fuse_req_t req, struct fuse_file_info *fi)
{
fuse_reply_open(req, fi);
}

static void fipsrng_read(fuse_req_t req, size_t size, off_t off,
struct fuse_file_info *fi)
{
char random[128];
ssize_t ret_bytes;

if (size > sizeof(random))
size = sizeof(random);

ret_bytes = read(rng, random, size);
if (ret_bytes < 0)
fuse_reply_err(req, errno);
else
fuse_reply_buf(req, random, ret_bytes);
}

static void fipsrng_write(fuse_req_t req, const char *buf, size_t size,
off_t off, struct fuse_file_info *fi)
{
/* Swallow it, we don't care. */
fuse_reply_write(req, size);
}

static void fipsrng_ioctl(fuse_req_t req, int cmd, void *arg,
struct fuse_file_info *fi, unsigned flags,
const void *in_buf, size_t in_bufsz, size_t out_bufsz)
{
/* TODO: implement the various RNG ioctls */
fuse_reply_err(req, ENOSYS);
}

static const struct cuse_lowlevel_ops fipsrng_clop = {
.open = fipsrng_open,
.read = fipsrng_read,
.write = fipsrng_write,
.ioctl = fipsrng_ioctl,
};

int main(int argc, char **argv)
{
static const struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "rng",
.salg_name = "jitterentropy_rng"
};
static const char *dev_info_argv[] = { "DEVNAME=jrandom" };
static const struct cuse_info ci = {
.dev_info_argc = 1,
.dev_info_argv = dev_info_argv,
.flags = CUSE_UNRESTRICTED_IOCTL
};
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
int ret = 1, afalg;

if (fuse_opt_parse(&args, NULL, NULL, NULL)) {
fprintf(stderr, "failed to parse options\n");
goto out;
}

afalg = socket(AF_ALG, SOCK_SEQPACKET, 0);
if (afalg < 0) {
perror("socket(AF_ALG)");
goto out;
}
if (bind(afalg, (const struct sockaddr *)&sa, sizeof(sa)) < 0) {
perror("bind(\"rng\", \"jitterentropy_rng\")");
goto out;
}
rng = accept(afalg, NULL, 0);
if (rng < 0) {
perror("accept()");
goto out;
}
ret = cuse_lowlevel_main(args.argc, args.argv, &ci, &fipsrng_clop, NULL);
out:
fuse_opt_free_args(&args);
return ret;
}

2022-01-10 22:20:18

by Jason A. Donenfeld

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Jan 10, 2022 at 9:18 PM Theodore Ts'o <[email protected]> wrote:
> In general, you need FIPS
> certification for some specific use cases / application. For example,
> if you're going for PCI compliance, then you might only need FIPS
> compliance for your OpenSSL library. What the FIPS certification lab
> might consider acceptable for its entropy for its DRBG is an
> interesting question. For some, simply having the OpenSSL library use
> RDSEED or RDRAND might be sufficient. Or it could talk to an actual
> physical RNG device.
>
> So disabling getrandom() is probably not necessary, just so long as
> you can demonstrate that the FIPS cryptographic module --- i.e., the
> OpenSSL library --- is getting its entropy from an acceptable source.

I don't know exactly what these people think they want, but what you
say seems probably correct.

> I suspect what's actually going on is that some enterprise customers
> have FIPS complaince on a check-off list, and they aren't actually
> getting a formal FIPS certification. Or they only need something to
> wave under the noses of their PCI certification company, and so the
> question is what makes them happy.

Right.

> And this is why some FIPS certification have gotten by just *fine*
> with a pure userspace OpenSSL library as their FIPS cryptographic
> module. Where you draw the line between a "blessed" entropy source
> and one that's just hand-waving is really at the discretion of the
> certification lab.

Hah, probably correct.

So, seen this way, and combined with the solution provided at [1] (or
similar) for people who think they need something there, it seems like
the FIPS people can likely get what they need without really needing
to involve the kernel anyway.

Jason

[1] https://lore.kernel.org/lkml/[email protected]/

2022-01-11 01:44:32

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator



On Mon, Jan 10, 2022, at 2:19 PM, Jason A. Donenfeld wrote:
> On Mon, Jan 10, 2022 at 9:18 PM Theodore Ts'o <[email protected]> wrote:
>> In general, you need FIPS
>> certification for some specific use cases / application. For example,
>> if you're going for PCI compliance, then you might only need FIPS
>> compliance for your OpenSSL library. What the FIPS certification lab
>> might consider acceptable for its entropy for its DRBG is an
>> interesting question. For some, simply having the OpenSSL library use
>> RDSEED or RDRAND might be sufficient. Or it could talk to an actual
>> physical RNG device.
>>
>> So disabling getrandom() is probably not necessary, just so long as
>> you can demonstrate that the FIPS cryptographic module --- i.e., the
>> OpenSSL library --- is getting its entropy from an acceptable source.
>
> I don't know exactly what these people think they want, but what you
> say seems probably correct.
>
>> I suspect what's actually going on is that some enterprise customers
>> have FIPS complaince on a check-off list, and they aren't actually
>> getting a formal FIPS certification. Or they only need something to
>> wave under the noses of their PCI certification company, and so the
>> question is what makes them happy.
>
> Right.
>
>> And this is why some FIPS certification have gotten by just *fine*
>> with a pure userspace OpenSSL library as their FIPS cryptographic
>> module. Where you draw the line between a "blessed" entropy source
>> and one that's just hand-waving is really at the discretion of the
>> certification lab.
>
> Hah, probably correct.
>
> So, seen this way, and combined with the solution provided at [1] (or
> similar) for people who think they need something there, it seems like
> the FIPS people can likely get what they need without really needing
> to involve the kernel anyway.

Hmm, cute, but I think we can do a bit better. After all, this hack involves trusting a whole lot of code that is *not* intended for secrets to avoid having side channels.

So let’s solve it for real. Have a driver (in a module) that exposes a /dev/urandom compatible interface to the CryptoAPI DRBG. We can do a really nice job of it, and maybe it’ll be 100 lines of code. People can do whatever they like with it in their container manager or boot scripts. And if it has a problem (where it’s *less* secure than the real urandom), we can say “I told you so”.

We can go one step farther: add an LSM hook to getrandom(). Then someone can hack up a fips_t policy for SELinux that turns off getrandom.

>
> Jason
>
> [1] https://lore.kernel.org/lkml/[email protected]/

2022-01-11 03:12:03

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Jan 10, 2022 at 05:44:03PM -0800, Andy Lutomirski wrote:
>
> So let’s solve it for real. Have a driver (in a module) that
> exposes a /dev/urandom compatible interface to the CryptoAPI DRBG.
> We can do a really nice job of it, and maybe it’ll be 100 lines of
> code. People can do whatever they like with it in their container
> manager or boot scripts. And if it has a problem (where it’s *less*
> secure than the real urandom), we can say “I told you so”.
>
> We can go one step farther: add an LSM hook to getrandom(). Then
> someone can hack up a fips_t policy for SELinux that turns off
> getrandom.

These are both dangerous. The first means creating a new device node
which effectively is /dev/drbg-random which could be bind mounted or
mknod'ed to be /dev/urandom. But if the user boots a kernel that
doesn't support this new device node, it will mean opening
/dev/urandom will get ENODEV.

Similarly, getrandom(2) never fails. By allowing a SELinux policy to
force it to fail with ENOSYS, or some other error, it means exposing
userspace code to a failure path that may not be as well tested.
Sure, *sane* code might fall back to opening /dev/urandom; but the
whole point of getrandom(2) was that it was a dumb, stupid interface
interface that could be safely used by application programmers. Not
paranoid OS crypto engineers that carefully check the error returns of
all system calls, with appropriate fallbacks and making sure that code
always "fails safe".

Right now, the enterprise distros are doing their own thing, and quite
frankly, I don't see a problem with that. If it turns out DRBG is
less secure (and there are some things that fill me with disquiet),
then let them take the economic consequences, since they are the ones
who are doing this for the economic advantages of trying to claim FIPS
compliance.

If we must support this in the upstream kernel, then configure it via
CONFIG_RANDOM_SECURITY_THEATRE which redirects getrandom(2) and
/dev/[u]random to DRBG. I'd prefer that it be possible for someone to
put "random_security_theatre=0" on the boot command line which would
disable redirecting the interfaces to DRBG so if it turns out that
DRBG *is* less secure, we can give advice on how to turn it off
without requiring a patched kernel. :-)

- Ted

2022-01-11 04:05:50

by Willy Tarreau

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Jan 10, 2022 at 10:10:15PM -0500, Theodore Ts'o wrote:
> If we must support this in the upstream kernel, then configure it via
> CONFIG_RANDOM_SECURITY_THEATRE which redirects getrandom(2) and
> /dev/[u]random to DRBG. I'd prefer that it be possible for someone to
> put "random_security_theatre=0" on the boot command line which would
> disable redirecting the interfaces to DRBG so if it turns out that
> DRBG *is* less secure, we can give advice on how to turn it off
> without requiring a patched kernel. :-)

In this case, why not do it the other way around ? Instead of having
yet-another config option, just indicate that fips-like randoms are
enabled at boot via "random_security_theatre=1". Distros have their
solution which can even be documented for their customers and that's
done. Nobody uses it by default, the name is discouraging enough, but
for those who know they want it, it's easy to turn it on, and at the
same time it delivers them the reminder about what all this really is.

Willy

2022-01-11 04:13:54

by Matthew Garrett

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Mon, Jan 10, 2022 at 10:10:15PM -0500, Theodore Ts'o wrote:

> Right now, the enterprise distros are doing their own thing, and quite
> frankly, I don't see a problem with that. If it turns out DRBG is
> less secure (and there are some things that fill me with disquiet),
> then let them take the economic consequences, since they are the ones
> who are doing this for the economic advantages of trying to claim FIPS
> compliance.

The goal is to identify a solution that avoids the enterprise kernels
needing to do their own thing. They're in a position to globally
LD_PRELOAD something to thunk getrandom() to improve compatibility if
they want to, and they're also able to define the expected level of
breakage if you enable FIPS mode. An approach that allows a single
kernel to provide different policies in different contexts (eg,
different namespaces could have different device nodes providing
/dev/random) makes it easier to configure that based on customer
requirements.

> If we must support this in the upstream kernel, then configure it via
> CONFIG_RANDOM_SECURITY_THEATRE which redirects getrandom(2) and
> /dev/[u]random to DRBG. I'd prefer that it be possible for someone to
> put "random_security_theatre=0" on the boot command line which would
> disable redirecting the interfaces to DRBG so if it turns out that
> DRBG *is* less secure, we can give advice on how to turn it off
> without requiring a patched kernel. :-)

The majority of enterprise customers don't need FIPS compliance, so all
that would happen in that case is that the vendors would flip the sense
of that config option and the docs for enterprise distros and mainline
would be out of sync. I understand that this is a situation where a
niche case is making life miserable for everyone else, and I understand
that this is a hole that the enterprise world has dug for itself, but
where there are people expressing a real tangible use case that exists
for reasons outside their control, it really feels like we should try to
find a solution that works for everyone.

2022-01-11 10:02:24

by Alexander E. Patrakov

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

(resending without HTML this time, sorry for a possible duplicate)
вт, 11 янв. 2022 г. в 09:13, Matthew Garrett <[email protected]>:
> The goal is to identify a solution that avoids the enterprise kernels
> needing to do their own thing. They're in a position to globally
> LD_PRELOAD something to thunk getrandom() to improve compatibility if
> they want to, and they're also able to define the expected level of
> breakage if you enable FIPS mode. An approach that allows a single
> kernel to provide different policies in different contexts (eg,
> different namespaces could have different device nodes providing
> /dev/random) makes it easier to configure that based on customer
> requirements.

LD_PRELOAD is not a solution because of containers (that need to be
modified to make use of the preloadable library) and statically-linked
binaries.

--
Alexander E. Patrakov

2022-01-11 13:06:43

by Jason A. Donenfeld

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Hi Andy,

On Tue, Jan 11, 2022 at 2:44 AM Andy Lutomirski <[email protected]> wrote:
> So let’s solve it for real. Have a driver (in a module) that

Um, let's not. This really isn't something the kernel needs to solve
here at all. There's a viable userspace solution. I see that the
discussion of something finally slightly technical (as opposed to just
compliance BS) has nerd sniped you a bit, but keep in mind what the
actual overall picture is. This isn't something that needs to be done.
My little CUSE thing (which I'm happy to develop out a bit more, even)
has the intent of fulfilling a compliance checkbox and nothing more.

Jason

2022-01-11 13:16:53

by Jason A. Donenfeld

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

Hi Ted,

On Tue, Jan 11, 2022 at 4:12 AM Theodore Ts'o <[email protected]> wrote:
> These are both dangerous. The first means creating a new device node
> which effectively is /dev/drbg-random which could be bind mounted or
> mknod'ed to be /dev/urandom. But if the user boots a kernel that
> doesn't support this new device node, it will mean opening
> /dev/urandom will get ENODEV.
>
> Similarly, getrandom(2) never fails. By allowing a SELinux policy to
> force it to fail with ENOSYS, or some other error, it means exposing
> userspace code to a failure path that may not be as well tested.
> Sure, *sane* code might fall back to opening /dev/urandom; but the
> whole point of getrandom(2) was that it was a dumb, stupid interface
> interface that could be safely used by application programmers. Not
> paranoid OS crypto engineers that carefully check the error returns of
> all system calls, with appropriate fallbacks and making sure that code
> always "fails safe".
>
> Right now, the enterprise distros are doing their own thing, and quite
> frankly, I don't see a problem with that.

I agree with you. I think enterprise distros ought to keep doing their
own thing here, and there's a clear solution that does this in
userspace, and also a pretty non-invasive patch from Marcelo to patch
the crap into the kernel need be.

I spent some time reading about FIPS certification, compliance, and
the requirements of various customers. One thing in particular leapt
out at me, which I think you've been saying over and over in this
thread but I didn't fully understand until this morning:

The goal is generally to have particular pieces of software or
particular solutions FIPS certified. And to do this, they start from
the top of the stack and move onward down. Most OSS software out there
today isn't really FIPS ready and oftentimes a full solution needs
modifications in one place or another. Other times, it's enough to
plug in the right userspace crypto libraries. And I noticed in looking
at things that are FIPS certified that random number generation tends
to go through a userspace abstraction layer. And, it looks like these
abstraction layers all have FIPS-able RNG hooks. You mentioned OpenSSL
earlier, and it looks like even libgcrypt and wolfSSL have an
abstraction layer for this.

In other words, it's not even so clear that people who need FIPS
compliance really need /dev/urandom and such to be FIPS compliant as
part of that. And the ones who think they do for whatever security
theater nonsense can happily load up that CUSE thing I made, apply a
deliberately-downstream patch, or whatever other clever solution.

So indeed it really doesn't seem like this is something the kernel
needs to be doing.

Jason

2022-01-11 15:10:49

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator



On Tue, Jan 11, 2022, at 5:06 AM, Jason A. Donenfeld wrote:
> Hi Andy,
>
> On Tue, Jan 11, 2022 at 2:44 AM Andy Lutomirski <[email protected]> wrote:
>> So let’s solve it for real. Have a driver (in a module) that
>
> Um, let's not. This really isn't something the kernel needs to solve
> here at all. There's a viable userspace solution. I see that the
> discussion of something finally slightly technical (as opposed to just
> compliance BS) has nerd sniped you a bit, but keep in mind what the
> actual overall picture is. This isn't something that needs to be done.
> My little CUSE thing (which I'm happy to develop out a bit more, even)
> has the intent of fulfilling a compliance checkbox and nothing more.
>


Can you develop your CUSE thing enough that it’s credibly safe against side channels? If so, fine.

I admit this is all rather absurd. FIPS aware userspace can do whatever it wants, and
It should be aware that /dev/urandom IS NOT FIPS. What’s the problem? rand(3) isn’t FIPS either, but no one puts person-years of effort into trying to paint it FIPS-colored

2022-01-11 16:10:46

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Tue, Jan 11, 2022 at 02:16:30PM +0100, Jason A. Donenfeld wrote:
> I spent some time reading about FIPS certification, compliance, and
> the requirements of various customers. One thing in particular leapt
> out at me, which I think you've been saying over and over in this
> thread but I didn't fully understand until this morning:
>
> The goal is generally to have particular pieces of software or
> particular solutions FIPS certified. And to do this, they start from
> the top of the stack and move onward down. Most OSS software out there
> today isn't really FIPS ready and oftentimes a full solution needs
> modifications in one place or another. Other times, it's enough to
> plug in the right userspace crypto libraries. And I noticed in looking
> at things that are FIPS certified that random number generation tends
> to go through a userspace abstraction layer. And, it looks like these
> abstraction layers all have FIPS-able RNG hooks. You mentioned OpenSSL
> earlier, and it looks like even libgcrypt and wolfSSL have an
> abstraction layer for this.

I know this thread is about security theatre, not real security, but
there's an even more important reason why the FIPS cryptographic
module should be as high in the stack as possible (e.g., in
userspace). Let's consider as, Albert Einstein would put it, the
following gedanken experiment:

Let's presume that in 2008, there was a FIPS-140 certified OS
designating the Linux kernel as the "cryptographic module", and so
/dev/urandom was hacked to be "FIPS certified". Huzzah! Now let's
assume that OS was using Ubuntu, which, being derived from Debian, was
subject to a distribution "value add" where in blind obedience to a
valgrind warning, there was a distro-level change which resulted in
OpenSSL on Debian and Debian derivitives generating extremely
predictable keys[1]. This caused fairly massive security problems for
any use of OpenSSL, including ssh, certificate generation, etc. ---
despite the FIPS 140 certification of the OS. Oops!

[1] https://en.wikinews.org/wiki/Predictable_random_number_generator_discovered_in_the_Debian_version_of_OpenSSL

It might be *cheaper* to claim that your OS is FIPS 140 certified by
hacking /dev/urandom. Otherwise, you might have to have a responsible
security engineer audit the various userspace cryptographic libraries,
and that would be way more expensive. It's much easier for a product
manager to say, "my work here is done" after applying a patch to the
Linux kernel for /dev/urandom, and not bothering to get an engineer to
certify the rest of the cryptographic stack.

And, if enterprise customers just care that an enterprise distro can
claim "FIPS 140 compliance", and they can push that claim of FIPS
compliance to the PCI certification authorities, they're happy. So
ultimately, this is really an economic requirement, not a security
requirement. And given that enterprise distros are the ones getting
paid $$$ in order to claim FIPS 140 compliance, then from an upstream
perspective, if our gaols is to optimzie the speed of getrandom(2) and
/dev/urandom, and to encourage application programmers to do the right
thing --- and *not* security theare for the sake of economic goals, we
should make technical decisions accordingly.

Cheers,

- Ted

2022-01-11 17:11:03

by Matthew Garrett

[permalink] [raw]
Subject: Re: [PATCH v43 01/15] Linux Random Number Generator

On Tue, Jan 11, 2022 at 02:57:54PM +0500, Alexander E. Patrakov wrote:

> LD_PRELOAD is not a solution because of containers and statically-linked
> binaries.

No, it doesn't solve all problems, but the question is whether it needs
to. We're talking about the scenario where:

a) a customer requires FIPS compliance, and
b) the customer has an app that calls getrandom() and doesn't fallback,
and
c) they're doing so with statically linked binaries or container
infrastructure that doesn't allow injection of other libraries

How common is this? Does the kernel need to solve this scenario?