1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter.git synced 2025-02-15 04:42:02 +00:00
openmptcprouter/6.12/target/linux/generic/hack-6.12/998-LRNG.patch
2024-11-06 10:17:15 +01:00

14106 lines
443 KiB
Diff

From a736ccf49716dde80660d93f32358b455662d1e1 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Tue, 25 Apr 2023 22:22:44 +0200
Subject: [PATCH 01/25] LRNG: Entropy Source and DRNG Manager
The kernel crypto API contains deterministic random number generators
(DRNG) which a caller must seed and reseed. The task of seeding a DRNG
is a non-trivial task requiring the consideration of a significant
number of aspects. The Entropy Source and DRNG Manager (ESDM) fills
that gap to transparently seed and reseed DRNGs. A user of the ESDM
obtains random numbers from an appropriately seeded and initialized
DRNG. Further, the ESDM controls various entropy sources guaranteeing
that they are properly initialized and managed.
The ESDM consists of two main parts:
- The entropy source (ES) manager implemented in lrng_es_mgr.c
controls the available entropy sources including pulling appropritate
amount of data from them for the DRNG manager.
- The DRNG manager provided with lrng_drng_mgr.c controls the DRNG(s)
and ensures proper seeding and reseeding.
The entropy source manager controls the entropy sources registered in
the lrng_es array. The entropy sources provide a function pointer data
structure that is used to obtain the services from it.
The ES manager triggers the initial seeding of the DRNGs during boot
time in three stages:
1. The DRNG is seeded from the entropy sources if all entropy sources
collectively have at least 32 bits of entropy available. The goal
of this step is to ensure that the DRNG receive some initial entropy
as early as possible.
2. The DRNG is reseeded from the entropy sources if all entropy sources
collectively have at least 128 bits of entropy available.
3. The DRNG is reseeded from the entropy sources if all entropy sources
collectively have at least 256 bits of entropy available.
At the time of the reseeding steps, the DRNG requests as much entropy as
is available in order to skip certain steps and reach the seeding level
of 256 bits. This may imply that one or more of the aforementioned steps
are skipped.
In all listed steps, the DRNG is (re)seeded with a number of random bytes
from the entropy pool that is at most the amount of entropy present in
the entropy pool. This means that for example when the entropy pool
contains 128 or more bits of entropy, the DRNG is seeded with that amount
of entropy as well.
Entropy sources (ES) inform the ES manager when new entropy has been
collected using the lrng_es_add_entropy() function. That function
schedules a DRNG (re)seed with the DRNG manager. When the DRNG manager
requests entropy data, the function lrng_fill_seed_buffer fills the seed
buffer by iterating through all available ES. The output of all entropy
sources is concatenated with each other. Further, the seed buffer
contains the amount of entropy each entropy credits its data. Finally a
time stamp is added.
The ES trigger such (re)seeding events only as long as not all DRNGs
are fully seeded with 256 bits of entropy. Once that seeding level is
reached, the triggers are not further processed.
The DRNG manager initializes the initial DRNG instance during late stage
of the kernel boot process before user space is triggered. The DRNG is
seeded at the following occasions:
- when the DRNG is initialized, the available amount of entropy is used,
- during boot time until the DRNG is fully initialized, the reaching of
the aforementioned seeding steps of 32/128/256 bits of entropy trigger
a reseed of the DRNG.
- at runtime after the elapse of 600 seconds since the last seeding,
the DRNG reseeding flag is raised
- at runtime when more than 2^20 generate operations were performed by
the given DRNG since last reseeding, the reseeding flag is raised
Raising the reseeding flag implies that the DRNG is seeded in process
context the next time a caller requests random numbers.
At runtime, the DRNG manager requires at least 128 bits of entropy from
the entropy sources (or 256 bits when the FIPS mode is active to be
SP800-90C compliant). It may be possible that the entropy sources may
not deliver that amount. The DRNG is reseeded with the available amount
of entropy and continues to operate. Yet, when after 2^30 generate
requests since the last seeding with 128 bits (or 256 bits in FIPS mode)
the DRNG cannot be seeded with 128 bits (or 256 bits), the DRNG becomes
unseeded which means it will not produce random numbers until it is
fully reseeded again.
To support the DRNG manager, a DRNG implementation is provided with
lrng_drng_kcapi.c. It uses the kernel crypto API RNG framework and
allows the specification of the used DRNG with the kernel command line
option of lrng_drng_kcapi.drng_name. If no reference is given, the
default is the SP800-90A DRBG. In case the chosen DRNG requires the seed
to have a certain length, a hash is used bring the entropy buffer into
the proper size.
In addition, the DRNG manager controls the message digest implementation
offered to entropy sources when they want to perform a conditioning
operation. As entropy sources may require the conditioning operation at
any time, the default is a SHA-256 software hash implementation that
neither sleeps nor does it need any memory allocation operation.
Therefore, this hash is available even for the earliest kernel
operations.
The initial drop of the ESDM includes the entropy source of the
"auxiliary" pool. This entropy source must always be present. It is an
entropy pool that is based on the state of a message digest. Every
insertion of data is a hash update operation. In order to obtain data, a
hash final operation is performed. The purpose of this auxiliary pool is
twofold:
- Provide a general interface to inject an arbitrary amount of data from
any external source. When providing such data, the caller may specify
the amount of entropy it contains.
- The auxiliary pool also provides the backtracking resistance for all
entropy sources. Once a seed buffer is filled from all entropy sources
it is re-inserted into the auxiliary pool at the same time it is used
for seeding the DRNG. Naturally, the insertion of the seed buffer into
the auxiliary pool is not credited with any entropy.
If enabled during compile time with the boot option of
fips=1, the entropy source oversampling is activated. The oversampling
pulls 128 more bits of entropy than originally requested. This implies
that when 256 bits of entropy are requested for a (re)seed of a DRNG,
the ES are queried for 384 bits. This oversampling complies with
SP800-90C.
This patch set contains a number of header files for subsequent
additions, but with the current Kconfig settings, these additional
settings will be folded to noops.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/Kconfig | 2 +
drivers/char/Makefile | 2 +
drivers/char/lrng/Kconfig | 1017 +++++++++++++++++
drivers/char/lrng/Makefile | 11 +
drivers/char/lrng/lrng_definitions.h | 163 +++
drivers/char/lrng/lrng_drng_atomic.h | 23 +
drivers/char/lrng/lrng_drng_chacha20.c | 195 ++++
drivers/char/lrng/lrng_drng_chacha20.h | 42 +
drivers/char/lrng/lrng_drng_drbg.h | 13 +
drivers/char/lrng/lrng_drng_kcapi.h | 13 +
drivers/char/lrng/lrng_drng_mgr.c | 742 ++++++++++++
drivers/char/lrng/lrng_drng_mgr.h | 86 ++
drivers/char/lrng/lrng_es_aux.c | 335 ++++++
drivers/char/lrng/lrng_es_aux.h | 44 +
drivers/char/lrng/lrng_es_cpu.h | 17 +
drivers/char/lrng/lrng_es_irq.h | 24 +
drivers/char/lrng/lrng_es_jent.h | 17 +
drivers/char/lrng/lrng_es_krng.h | 17 +
drivers/char/lrng/lrng_es_mgr.c | 506 ++++++++
drivers/char/lrng/lrng_es_mgr.h | 56 +
drivers/char/lrng/lrng_es_mgr_cb.h | 87 ++
drivers/char/lrng/lrng_es_sched.h | 20 +
drivers/char/lrng/lrng_es_timer_common.h | 83 ++
drivers/char/lrng/lrng_interface_dev_common.h | 51 +
.../char/lrng/lrng_interface_random_kernel.h | 17 +
drivers/char/lrng/lrng_numa.h | 11 +
drivers/char/lrng/lrng_sha.h | 14 +
drivers/char/lrng/lrng_sha1.c | 88 ++
drivers/char/lrng/lrng_sha256.c | 72 ++
drivers/char/lrng/lrng_sysctl.h | 15 +
include/linux/lrng.h | 251 ++++
31 files changed, 4034 insertions(+)
create mode 100644 drivers/char/lrng/Kconfig
create mode 100644 drivers/char/lrng/Makefile
create mode 100644 drivers/char/lrng/lrng_definitions.h
create mode 100644 drivers/char/lrng/lrng_drng_atomic.h
create mode 100644 drivers/char/lrng/lrng_drng_chacha20.c
create mode 100644 drivers/char/lrng/lrng_drng_chacha20.h
create mode 100644 drivers/char/lrng/lrng_drng_drbg.h
create mode 100644 drivers/char/lrng/lrng_drng_kcapi.h
create mode 100644 drivers/char/lrng/lrng_drng_mgr.c
create mode 100644 drivers/char/lrng/lrng_drng_mgr.h
create mode 100644 drivers/char/lrng/lrng_es_aux.c
create mode 100644 drivers/char/lrng/lrng_es_aux.h
create mode 100644 drivers/char/lrng/lrng_es_cpu.h
create mode 100644 drivers/char/lrng/lrng_es_irq.h
create mode 100644 drivers/char/lrng/lrng_es_jent.h
create mode 100644 drivers/char/lrng/lrng_es_krng.h
create mode 100644 drivers/char/lrng/lrng_es_mgr.c
create mode 100644 drivers/char/lrng/lrng_es_mgr.h
create mode 100644 drivers/char/lrng/lrng_es_mgr_cb.h
create mode 100644 drivers/char/lrng/lrng_es_sched.h
create mode 100644 drivers/char/lrng/lrng_es_timer_common.h
create mode 100644 drivers/char/lrng/lrng_interface_dev_common.h
create mode 100644 drivers/char/lrng/lrng_interface_random_kernel.h
create mode 100644 drivers/char/lrng/lrng_numa.h
create mode 100644 drivers/char/lrng/lrng_sha.h
create mode 100644 drivers/char/lrng/lrng_sha1.c
create mode 100644 drivers/char/lrng/lrng_sha256.c
create mode 100644 drivers/char/lrng/lrng_sysctl.h
create mode 100644 include/linux/lrng.h
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -422,4 +422,6 @@ config ADI
and SSM (Silicon Secured Memory). Intended consumers of this
driver include crash and makedumpfile.
+source "drivers/char/lrng/Kconfig"
+
endmenu
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -43,3 +43,5 @@ obj-$(CONFIG_PS3_FLASH) += ps3flash.o
obj-$(CONFIG_XILLYBUS_CLASS) += xillybus/
obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o
obj-$(CONFIG_ADI) += adi.o
+
+obj-$(CONFIG_LRNG) += lrng/
--- /dev/null
+++ b/drivers/char/lrng/Kconfig
@@ -0,0 +1,1017 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Linux Random Number Generator configuration
+#
+
+config RANDOM_DEFAULT_IMPL
+ bool "Kernel RNG Default Implementation"
+ default y
+ help
+ The default random number generator as provided with
+ drivers/char/random.c is selected with this option.
+
+config LRNG_AUTO_SELECTED
+ bool
+ default y if !RANDOM_DEFAULT_IMPL
+ default n if RANDOM_DEFAULT_IMPL
+ select LRNG
+
+config LRNG
+ bool "Linux Random Number Generator"
+ default n
+ select CRYPTO_LIB_SHA256 if CRYPTO
+ help
+ The Linux Random Number Generator (LRNG) generates entropy
+ from different entropy sources. Each entropy source can
+ be enabled and configured independently. The interrupt
+ entropy source can be configured to be SP800-90B compliant.
+ The entire LRNG can be configured to be SP800-90C compliant.
+ Runtime-switchable cryptographic support is available.
+ The LRNG delivers significant entropy during boot.
+
+ The LRNG also provides compliance to SP800-90A/B/C.
+
+menu "Linux Random Number Generator Configuration"
+ depends on LRNG
+
+if LRNG
+
+config LRNG_SHA256
+ bool
+ default y if CRYPTO_LIB_SHA256
+
+config LRNG_SHA1
+ bool
+ default y if !CRYPTO_LIB_SHA256
+
+config LRNG_COMMON_DEV_IF
+ bool
+
+config LRNG_DRNG_ATOMIC
+ bool
+ select LRNG_DRNG_CHACHA20
+
+config LRNG_SYSCTL
+ bool
+ depends on SYSCTL
+
+config LRNG_RANDOM_IF
+ bool
+ default n if RANDOM_DEFAULT_IMPL
+ default y if !RANDOM_DEFAULT_IMPL
+ select LRNG_COMMON_DEV_IF
+ select LRNG_DRNG_ATOMIC
+ select LRNG_SYSCTL
+
+menu "Specific DRNG seeding strategies"
+
+config LRNG_AIS2031_NTG1_SEEDING_STRATEGY
+ bool "AIS 20/31 NTG.1 seeding strategy"
+ default n
+ help
+ When enabling this option, two entropy sources must
+ deliver 220 bits of entropy each to consider a DRNG
+ as fully seeded. Any two entropy sources can be used
+ to fulfill this requirement. If specific entropy sources
+ shall not be capable of contributing to this seeding
+ strategy, the respective entropy source must be configured
+ to provide less than 220 bits of entropy.
+
+ The strategy is consistent with the requirements for
+ NTG.1 compliance in German AIS 20/31 draft from 2022
+ and is only enforced with lrng_es_mgr.ntg1=1.
+
+ Compliance with German AIS 20/31 from 2011 is always
+ present when using /dev/random with the flag O_SYNC or
+ getrandom(2) with GRND_RANDOM.
+
+ If unsure, say N.
+
+endmenu # "Specific DRNG seeding strategies"
+
+# menu "LRNG Interfaces"
+#
+# config LRNG_KCAPI_IF
+# tristate "Interface with Kernel Crypto API"
+# depends on CRYPTO_RNG
+# help
+# The LRNG can be registered with the kernel crypto API's
+# random number generator framework. This offers a random
+# number generator with the name "lrng" and a priority that
+# is intended to be higher than the existing RNG
+# implementations.
+#
+# config LRNG_HWRAND_IF
+# tristate "Interface with Hardware Random Number Generator Framework"
+# depends on HW_RANDOM
+# select LRNG_DRNG_ATOMIC
+# help
+# The LRNG can be registered with the hardware random number
+# generator framework. This offers a random number generator
+# with the name "lrng" that is accessible via the framework.
+# For example it allows pulling data from the LRNG via the
+# /dev/hwrng file.
+#
+# config LRNG_DEV_IF
+# bool "Character device file interface"
+# select LRNG_COMMON_DEV_IF
+# help
+# The LRNG can create a character device file that operates
+# identically to /dev/random including IOCTL, read and write
+# operations.
+#
+# endmenu # "LRNG Interfaces"
+
+# menu "Entropy Source Configuration"
+#
+# 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.
+#
+# Each entropy source allows its entropy rate changed with
+# a kernel command line option. When not providing any
+# option, the default specified during kernel compilation
+# is applied.
+#
+# comment "Common Timer-based Entropy Source Configuration"
+#
+# config LRNG_IRQ_DFLT_TIMER_ES
+# bool
+#
+# config LRNG_SCHED_DFLT_TIMER_ES
+# bool
+#
+# config LRNG_TIMER_COMMON
+# bool
+#
+# choice
+# prompt "Default Timer-based Entropy Source"
+# default LRNG_IRQ_DFLT_TIMER_ES
+# depends on LRNG_TIMER_COMMON
+# help
+# Select the timer-based entropy source that is credited
+# with entropy. The other timer-based entropy sources may
+# be operational and provide data, but are credited with no
+# entropy.
+#
+# config LRNG_IRQ_DFLT_TIMER_ES
+# bool "Interrupt Entropy Source"
+# depends on LRNG_IRQ
+# help
+# The interrupt entropy source is selected as a timer-based
+# entropy source to provide entropy.
+#
+# config LRNG_SCHED_DFLT_TIMER_ES
+# bool "Scheduler Entropy Source"
+# depends on LRNG_SCHED
+# help
+# The scheduler entropy source is selected as timer-based
+# entropy source to provide entropy.
+# endchoice
+#
+# choice
+# prompt "LRNG Entropy Collection Pool Size"
+# default LRNG_COLLECTION_SIZE_1024
+# depends on LRNG_TIMER_COMMON
+# help
+# Select the size of the LRNG entropy collection pool
+# storing data for the interrupt as well as the scheduler
+# entropy sources 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 !CRYPTO_FIPS
+# bool "32 interrupt events"
+#
+# config LRNG_COLLECTION_SIZE_256
+# depends on !CRYPTO_FIPS
+# 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_HEALTH_TESTS
+# bool "Enable internal entropy source online health tests"
+# depends on LRNG_TIMER_COMMON
+# help
+# The online health tests applied to the interrupt entropy
+# source and to the scheduler entropy source to 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 both, the scheduler and the interrupt entropy sources,
+# are enabled, the health tests for both are applied
+# independent of each other.
+#
+# 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.1 - significance level 2^-80
+# config LRNG_RCT_CUTOFF_PERMANENT
+# int
+# default 81 if !LRNG_RCT_BROKEN
+# default 2 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
+#
+# Default taken from SP800-90B sec 4.4.2 - significance level 2^-80
+# config LRNG_APT_CUTOFF_PERMANENT
+# int
+# default 371 if !LRNG_APT_BROKEN
+# default 33 if LRNG_APT_BROKEN
+#
+# comment "Interrupt Entropy Source"
+#
+# config LRNG_IRQ
+# bool "Enable Interrupt Entropy Source as LRNG Seed Source"
+# default y
+# depends on !RANDOM_DEFAULT_IMPL
+# select LRNG_TIMER_COMMON
+# 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 an 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 LRNG IRQ ES 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.
+#
+# config LRNG_IRQ_ENTROPY_RATE
+# int "Interrupt Entropy Source Entropy Rate"
+# depends on LRNG_IRQ
+# range 256 4294967295 if LRNG_IRQ_DFLT_TIMER_ES
+# range 4294967295 4294967295 if !LRNG_IRQ_DFLT_TIMER_ES
+# default 256 if LRNG_IRQ_DFLT_TIMER_ES
+# default 4294967295 if !LRNG_IRQ_DFLT_TIMER_ES
+# help
+# The LRNG will collect the configured number of interrupts to
+# obtain 256 bits of entropy. This value can be set to any between
+# 256 and 4294967295. The LRNG guarantees that this value is not
+# lower than 256. This lower limit implies that one interrupt event
+# is credited with one bit of entropy. This value is subject to the
+# increase by the oversampling factor, if no high-resolution timer
+# is found.
+#
+# In order to effectively disable the interrupt entropy source,
+# the option has to be set to 4294967295. In this case, the
+# interrupt entropy source will still deliver data but without
+# being credited with entropy.
+#
+# comment "Jitter RNG Entropy Source"
+#
+# config LRNG_JENT
+# bool "Enable Jitter RNG as LRNG Seed Source"
+# depends on CRYPTO
+# select CRYPTO_JITTERENTROPY
+# help
+# The LRNG may use the Jitter RNG as entropy 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.
+#
+#choice
+# prompt "Jitter RNG Async Block Number"
+# default LRNG_JENT_ENTROPY_BLOCKS_NO_128
+# depends on LRNG_JENT
+# help
+# Select the number of Jitter RNG entropy blocks the asynchronous
+# collection operation will fill. A caller for Jitter RNG entropy
+# will be given data from the pre-filled blocks if available to
+# prevent the Jitter RNG from utilizing the CPU too much in a
+# possible hot code path.
+#
+# The number specifies the number of 256/384 bit blocks that will
+# be held in memory and asynchronously filled with Jitter RNG data.
+#
+# The asynchronous entropy collection can also be disabled at
+# kernel startup time when setting the command line option of
+# lrng_es_jent.jent_async_enabled=0. Also, setting this option at
+# runtime is allowed via the corresponding SysFS interface. This
+# option is only available with the options SysFS and
+# CONFIG_LRNG_RUNTIME_ES_CONFIG enabled.
+#
+# config LRNG_JENT_ENTROPY_BLOCKS_DISABLED
+# bool "Async collection disabled"
+#
+# # Any block number is allowed, provided it is a power of 2 and
+# # equal or larger than 4 (4 is due to the division in
+# # lrng_jent_async_get when deciding to wake up the monitor).
+# config LRNG_JENT_ENTROPY_BLOCKS_NO_32
+# bool "32 blocks"
+#
+# config LRNG_JENT_ENTROPY_BLOCKS_NO_64
+# bool "64 blocks"
+#
+# config LRNG_JENT_ENTROPY_BLOCKS_NO_128
+# bool "128 blocks (default)"
+#
+# config LRNG_JENT_ENTROPY_BLOCKS_NO_256
+# bool "256 blocks"
+#
+# config LRNG_JENT_ENTROPY_BLOCKS_NO_512
+# bool "512 blocks"
+#
+# config LRNG_JENT_ENTROPY_BLOCKS_NO_1024
+# bool "1024 blocks"
+#
+#endchoice
+#
+#config LRNG_JENT_ENTROPY_BLOCKS
+# int
+# default 0 if LRNG_JENT_ENTROPY_BLOCKS_DISABLED
+# default 32 if LRNG_JENT_ENTROPY_BLOCKS_NO_32
+# default 64 if LRNG_JENT_ENTROPY_BLOCKS_NO_64
+# default 128 if LRNG_JENT_ENTROPY_BLOCKS_NO_128
+# default 256 if LRNG_JENT_ENTROPY_BLOCKS_NO_256
+# default 512 if LRNG_JENT_ENTROPY_BLOCKS_NO_512
+# default 1024 if LRNG_JENT_ENTROPY_BLOCKS_NO_1024
+#
+# 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
+# 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.
+#
+# comment "Scheduler Entropy Source"
+#
+# config LRNG_SCHED
+# bool "Enable Scheduer Entropy Source as LRNG Seed Source"
+# select LRNG_TIMER_COMMON
+# help
+# The LRNG models an entropy source based on the timing of the
+# occurrence of scheduler-triggered context switches. Enable
+# this option to enable this scheduler entropy source.
+#
+# The scheduler entropy source is triggered every time a
+# context switch is triggered thus causes the scheduler to
+# execute slightly longer. Disabling the scheduler entropy
+# source implies that the performance penalty on the scheduler
+# added by the LRNG is eliminated. Yet, this entropy source is
+# considered to be an 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 scheduler 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.
+#
+# config LRNG_SCHED_ENTROPY_RATE
+# int "Scheduler Entropy Source Entropy Rate"
+# depends on LRNG_SCHED
+# range 256 4294967295 if LRNG_SCHED_DFLT_TIMER_ES
+# range 4294967295 4294967295 if !LRNG_SCHED_DFLT_TIMER_ES
+# default 256 if LRNG_SCHED_DFLT_TIMER_ES
+# default 4294967295 if !LRNG_SCHED_DFLT_TIMER_ES
+# help
+# The LRNG will collect the configured number of context switches
+# triggered by the scheduler 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 scheduler entropy source,
+# the option has to be set to 4294967295. In this case, the
+# scheduler entropy source will still deliver data but without
+# being credited with entropy.
+#
+# comment "Kernel RNG Entropy Source"
+#
+# config LRNG_KERNEL_RNG
+# bool "Enable Kernel RNG as LRNG Seed Source"
+# depends on RANDOM_DEFAULT_IMPL
+# help
+# The LRNG may use the kernel RNG (random.c) as entropy
+# source.
+#
+# config LRNG_KERNEL_RNG_ENTROPY_RATE
+# int "Kernel RNG Entropy Source Entropy Rate"
+# depends on LRNG_KERNEL_RNG
+# range 0 256
+# default 256
+# help
+# The option defines the amount of entropy the LRNG applies to 256
+# bits of data obtained from the kernel 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 kernel RNG entropy source
+# will provide 256 bits of data without being credited to contain
+# entropy.
+#
+# Note: This value is set to 0 automatically when booting the
+# kernel in FIPS mode (with fips=1 kernel command line option).
+# This is due to the fact that random.c is not SP800-90B
+# compliant.
+#
+# endmenu # "Entropy Source Configuration"
+
+config LRNG_DRNG_CHACHA20
+ tristate
+
+# config LRNG_DRBG
+# tristate
+# depends on CRYPTO
+# select CRYPTO_DRBG_MENU
+#
+# config LRNG_DRNG_KCAPI
+# tristate
+# depends on CRYPTO
+# select CRYPTO_RNG
+#
+# config LRNG_SWITCH
+# bool
+#
+# menuconfig LRNG_SWITCH_HASH
+# bool "Support conditioning hash runtime switching"
+# select LRNG_SWITCH
+# help
+# The LRNG uses a default message digest. With this
+# configuration option other message digests can be selected
+# and loaded at runtime.
+#
+# if LRNG_SWITCH_HASH
+#
+# config LRNG_HASH_KCAPI
+# tristate "Kernel crypto API hashing support for LRNG"
+# select CRYPTO_HASH
+# select CRYPTO_SHA512
+# help
+# Enable the kernel crypto API support for entropy compression
+# and conditioning functions.
+#
+# endif # LRNG_SWITCH_HASH
+#
+# menuconfig LRNG_SWITCH_DRNG
+# bool "Support DRNG runtime switching"
+# select LRNG_SWITCH
+# help
+# The LRNG uses a default DRNG With this configuration
+# option other DRNGs or message digests can be selected and
+# loaded at runtime.
+#
+# if LRNG_SWITCH_DRNG
+#
+# config LRNG_SWITCH_DRNG_CHACHA20
+# tristate "ChaCha20-based DRNG support for LRNG"
+# depends on !LRNG_DFLT_DRNG_CHACHA20
+# select LRNG_DRNG_CHACHA20
+# help
+# Enable the ChaCha20-based DRNG. This DRNG implementation
+# does not depend on the kernel crypto API presence.
+#
+# config LRNG_SWITCH_DRBG
+# tristate "SP800-90A support for the LRNG"
+# depends on !LRNG_DFLT_DRNG_DRBG
+# select LRNG_DRBG
+# 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.
+#
+# config LRNG_SWITCH_DRNG_KCAPI
+# tristate "Kernel Crypto API support for the LRNG"
+# depends on !LRNG_DFLT_DRNG_KCAPI
+# depends on !LRNG_SWITCH_DRBG
+# select LRNG_DRNG_KCAPI
+# 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_SWITCH_DRNG
+
+choice
+ prompt "LRNG Default DRNG"
+ default LRNG_DFLT_DRNG_CHACHA20
+ help
+ Select the default deterministic random number generator
+ that is used by the LRNG. When enabling the switchable
+ cryptographic mechanism support, this DRNG can be
+ replaced at runtime.
+
+ config LRNG_DFLT_DRNG_CHACHA20
+ bool "ChaCha20-based DRNG"
+ select LRNG_DRNG_CHACHA20
+
+# config LRNG_DFLT_DRNG_DRBG
+# depends on RANDOM_DEFAULT_IMPL
+# bool "SP800-90A DRBG"
+# select LRNG_DRBG
+#
+# config LRNG_DFLT_DRNG_KCAPI
+# depends on RANDOM_DEFAULT_IMPL
+# bool "Kernel Crypto API DRNG"
+# select LRNG_DRNG_KCAPI
+endchoice
+
+# 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_TESTING
+# bool
+#
+# config LRNG_TESTING_RECORDING
+# bool
+#
+# comment "Interrupt Entropy Source Test Interfaces"
+#
+# config LRNG_RAW_HIRES_ENTROPY
+# bool "Interface to obtain raw unprocessed IRQ noise source data"
+# default y
+# depends on LRNG_IRQ
+# select LRNG_TESTING
+# select LRNG_TESTING_RECORDING
+# 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 "Entropy test interface to Jiffies of IRQ noise source"
+# depends on LRNG_IRQ
+# select LRNG_TESTING
+# select LRNG_TESTING_RECORDING
+# 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 "Entropy test interface to IRQ number noise source"
+# depends on LRNG_IRQ
+# select LRNG_TESTING
+# select LRNG_TESTING_RECORDING
+# help
+# The test interface allows a privileged process to capture
+# the raw unconditioned interrupt number 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_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_RETIP_ENTROPY
+# bool "Entropy test interface to RETIP value of IRQ noise source"
+# depends on LRNG_IRQ
+# select LRNG_TESTING
+# select LRNG_TESTING_RECORDING
+# 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.
+# 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 "Entropy test interface to IRQ register value noise source"
+# depends on LRNG_IRQ
+# select LRNG_TESTING
+# select LRNG_TESTING_RECORDING
+# 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 "Test interface to LRNG raw entropy IRQ storage array"
+# depends on LRNG_IRQ
+# select LRNG_TESTING
+# select LRNG_TESTING_RECORDING
+# 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 "LRNG interrupt entropy source performance monitor"
+# depends on LRNG_IRQ
+# select LRNG_TESTING
+# select LRNG_TESTING_RECORDING
+# 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.
+#
+# comment "Scheduler Entropy Source Test Interfaces"
+#
+# config LRNG_RAW_SCHED_HIRES_ENTROPY
+# bool "Interface to obtain raw unprocessed scheduler noise source data"
+# depends on LRNG_SCHED
+# select LRNG_TESTING
+# select LRNG_TESTING_RECORDING
+# 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 the Scheduler-based noise source
+# for statistical analysis. Extracted noise data is not used to
+# seed the LRNG.
+#
+# The raw noise data can be obtained using the lrng_raw_sched_hires
+# debugfs file. Using the option
+# lrng_testing.boot_raw_sched_hires_test=1 the raw noise of the
+# first 1000 entropy events since boot can be sampled.
+#
+# config LRNG_RAW_SCHED_PID_ENTROPY
+# bool "Entropy test interface to PID value"
+# depends on LRNG_SCHED
+# select LRNG_TESTING
+# select LRNG_TESTING_RECORDING
+# help
+# The test interface allows a privileged process to capture
+# the raw unconditioned PID 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_sched_pid debugfs file. Using the option
+# lrng_testing.boot_raw_sched_pid_test=1
+# the raw noise of the first 1000 entropy events since boot
+# can be sampled.
+#
+# config LRNG_RAW_SCHED_START_TIME_ENTROPY
+# bool "Entropy test interface to task start time value"
+# depends on LRNG_SCHED
+# select LRNG_TESTING
+# select LRNG_TESTING_RECORDING
+# help
+# The test interface allows a privileged process to capture
+# the raw unconditioned task start time 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_sched_starttime debugfs file. Using the option
+# lrng_testing.boot_raw_sched_starttime_test=1
+# the raw noise of the first 1000 entropy events since boot
+# can be sampled.
+#
+#
+# config LRNG_RAW_SCHED_NVCSW_ENTROPY
+# bool "Entropy test interface to task context switch numbers"
+# depends on LRNG_SCHED
+# select LRNG_TESTING
+# select LRNG_TESTING_RECORDING
+# help
+# The test interface allows a privileged process to capture
+# the raw unconditioned task numbers of context switches that
+# are 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_sched_nvcsw debugfs file. Using the option
+# lrng_testing.boot_raw_sched_nvcsw_test=1
+# the raw noise of the first 1000 entropy events since boot
+# can be sampled.
+#
+# config LRNG_SCHED_PERF
+# bool "LRNG scheduler entropy source performance monitor"
+# depends on LRNG_SCHED
+# select LRNG_TESTING
+# select LRNG_TESTING_RECORDING
+# help
+# With this option, the performance monitor of the LRNG
+# scheduler event handling code is enabled. The file provides
+# the execution time of the interrupt handler in cycles.
+#
+# The scheduler performance data can be obtained using
+# the lrng_sched_perf debugfs file. Using the option
+# lrng_testing.boot_sched_perf=1 the performance data of
+# the first 1000 entropy events since boot can be sampled.
+#
+# comment "Auxiliary Test Interfaces"
+#
+# config LRNG_ACVT_HASH
+# bool "Enable LRNG ACVT Hash interface"
+# select LRNG_TESTING
+# 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_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_RUNTIME_FORCE_SEEDING_DISABLE
+# bool "Enable runtime configuration of force seeding"
+# help
+# When enabling this option, the LRNG provides an interface
+# allowing the disabling of the force seeding when the DRNG
+# is not fully seeded but entropy is available.
+#
+# 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.
+#
+# 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
+
+endmenu # LRNG
--- /dev/null
+++ b/drivers/char/lrng/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Entropy Source and DRNG Manager.
+#
+
+obj-y += lrng_es_mgr.o lrng_drng_mgr.o \
+ lrng_es_aux.o
+obj-$(CONFIG_LRNG_SHA256) += lrng_sha256.o
+obj-$(CONFIG_LRNG_SHA1) += lrng_sha1.o
+
+obj-$(CONFIG_LRNG_DRNG_CHACHA20) += lrng_drng_chacha20.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_definitions.h
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022 - 2023, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_DEFINITIONS_H
+#define _LRNG_DEFINITIONS_H
+
+#include <crypto/sha1.h>
+#include <crypto/sha2.h>
+#include <linux/slab.h>
+
+/*************************** General LRNG parameter ***************************/
+
+/*
+ * Specific settings for different use cases
+ */
+#ifdef CONFIG_CRYPTO_FIPS
+# define LRNG_OVERSAMPLE_ES_BITS 64
+# define LRNG_SEED_BUFFER_INIT_ADD_BITS 128
+#else /* CONFIG_CRYPTO_FIPS */
+# define LRNG_OVERSAMPLE_ES_BITS 0
+# define LRNG_SEED_BUFFER_INIT_ADD_BITS 0
+#endif /* CONFIG_CRYPTO_FIPS */
+
+/* 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_INIT_SEED_SIZE_BITS \
+ (LRNG_DRNG_SECURITY_STRENGTH_BITS + 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
+
+/* AIS20/31: NTG.1.4 minimum entropy rate for one entropy source*/
+#define LRNG_AIS2031_NPTRNG_MIN_ENTROPY 220
+
+/*
+ * 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_LRNG_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_SWITCH
+# define LRNG_MAX_DIGESTSIZE 64
+#else
+# define LRNG_MAX_DIGESTSIZE LRNG_ATOMIC_DIGEST_SIZE
+#endif
+
+/*
+ * Oversampling factor of timer-based 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|SCHED]_ENTROPY_BITS divided by
+ * LRNG_ES_OVERSAMPLING_FACTOR.
+ *
+ * This value is allowed to be changed.
+ */
+#define LRNG_ES_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)
+
+/*
+ * Identification of a permanent health falure.
+ *
+ * Allow the given number of back-to-back health failures until incuring a
+ * permanent health failure. The chosen value implies an alpha of 2^-60
+ * considering that the alpha of one health failure is 2^-30
+ */
+#define LRNG_PERMANENT_HEALTH_FAILURES 2
+
+/****************************** Helper code ***********************************/
+
+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;
+}
+
+/* Convert entropy in bits into nr. of events with the same entropy content. */
+static inline u32 lrng_entropy_to_data(u32 entropy_bits, u32 entropy_rate)
+{
+ return ((entropy_bits * entropy_rate) /
+ LRNG_DRNG_SECURITY_STRENGTH_BITS);
+}
+
+/* Convert number of events into entropy value. */
+static inline u32 lrng_data_to_entropy(u32 num, u32 entropy_rate)
+{
+ return ((num * LRNG_DRNG_SECURITY_STRENGTH_BITS) /
+ entropy_rate);
+}
+
+static inline u32 atomic_read_u32(atomic_t *v)
+{
+ return (u32)atomic_read(v);
+}
+
+#endif /* _LRNG_DEFINITIONS_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_drng_atomic.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_DRNG_ATOMIC_H
+#define _LRNG_DRNG_ATOMIC_H
+
+#include "lrng_drng_mgr.h"
+
+#ifdef CONFIG_LRNG_DRNG_ATOMIC
+void lrng_drng_atomic_reset(void);
+void lrng_drng_atomic_seed_drng(struct lrng_drng *drng);
+void lrng_drng_atomic_force_reseed(void);
+struct lrng_drng *lrng_get_atomic(void);
+#else /* CONFIG_LRNG_DRNG_ATOMIC */
+static inline void lrng_drng_atomic_reset(void) { }
+static inline void lrng_drng_atomic_seed_drng(struct lrng_drng *drng) { }
+static inline void lrng_drng_atomic_force_reseed(void) { }
+static inline struct lrng_drng *lrng_get_atomic(void) { return NULL; }
+#endif /* CONFIG_LRNG_DRNG_ATOMIC */
+
+#endif /* _LRNG_DRNG_ATOMIC_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_drng_chacha20.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Backend for the LRNG providing the cryptographic primitives using
+ * ChaCha20 cipher implementations.
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#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_drng_chacha20.h"
+
+/******************************* ChaCha20 DRNG *******************************/
+
+#define CHACHA_BLOCK_WORDS (CHACHA_BLOCK_SIZE / sizeof(u32))
+
+/*
+ * 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;
+}
+
+/*
+ * 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_rfc7539(&state->block);
+
+ return state;
+}
+
+static void lrng_cc20_drng_dealloc(void *drng)
+{
+ struct chacha20_state *chacha20_state = (struct chacha20_state *)drng;
+
+ pr_debug("ChaCha20 core zeroized and freed\n");
+ kfree_sensitive(chacha20_state);
+}
+
+static const char *lrng_cc20_drng_name(void)
+{
+ return "ChaCha20 DRNG";
+}
+
+const struct lrng_drng_cb lrng_cc20_drng_cb = {
+ .drng_name = lrng_cc20_drng_name,
+ .drng_alloc = lrng_cc20_drng_alloc,
+ .drng_dealloc = lrng_cc20_drng_dealloc,
+ .drng_seed = lrng_cc20_drng_seed_helper,
+ .drng_generate = lrng_cc20_drng_generate_helper,
+};
+
+#if !defined(CONFIG_LRNG_DFLT_DRNG_CHACHA20) && \
+ !defined(CONFIG_LRNG_DRNG_ATOMIC)
+static int __init lrng_cc20_drng_init(void)
+{
+ return lrng_set_drng_cb(&lrng_cc20_drng_cb);
+}
+
+static void __exit lrng_cc20_drng_exit(void)
+{
+ lrng_set_drng_cb(NULL);
+}
+
+late_initcall(lrng_cc20_drng_init);
+module_exit(lrng_cc20_drng_exit);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
+MODULE_DESCRIPTION("Entropy Source and DRNG Manager - ChaCha20-based DRNG backend");
+#endif /* CONFIG_LRNG_DFLT_DRNG_CHACHA20 */
--- /dev/null
+++ b/drivers/char/lrng/lrng_drng_chacha20.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * LRNG ChaCha20 definitions
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_CHACHA20_H
+#define _LRNG_CHACHA20_H
+
+#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];
+};
+
+struct chacha20_state {
+ struct chacha20_block block;
+};
+
+static inline void lrng_cc20_init_rfc7539(struct chacha20_block *chacha20)
+{
+ chacha_init_consts(chacha20->constants);
+}
+
+#define LRNG_CC20_INIT_RFC7539(x) \
+ x.constants[0] = 0x61707865, \
+ x.constants[1] = 0x3320646e, \
+ x.constants[2] = 0x79622d32, \
+ x.constants[3] = 0x6b206574,
+
+extern const struct lrng_drng_cb lrng_cc20_drng_cb;
+
+#endif /* _LRNG_CHACHA20_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_drng_drbg.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * LRNG SP800-90A definitions
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_DRBG_H
+#define _LRNG_DRBG_H
+
+extern const struct lrng_drng_cb lrng_drbg_cb;
+
+#endif /* _LRNG_DRBG_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_drng_kcapi.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * LRNG kernel crypto API DRNG definition
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_KCAPI_DRNG_H
+#define _LRNG_KCAPI_DRNG_H
+
+extern const struct lrng_drng_cb lrng_kcapi_drng_cb;
+
+#endif /* _LRNG_KCAPI_DRNG_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_drng_mgr.c
@@ -0,0 +1,742 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG DRNG management
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/lrng.h>
+#include <linux/fips.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#include "lrng_drng_atomic.h"
+#include "lrng_drng_chacha20.h"
+#include "lrng_drng_drbg.h"
+#include "lrng_drng_kcapi.h"
+#include "lrng_drng_mgr.h"
+#include "lrng_es_aux.h"
+#include "lrng_es_mgr.h"
+#include "lrng_interface_random_kernel.h"
+#include "lrng_numa.h"
+#include "lrng_sha.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;
+
+/*
+ * Is LRNG for general-purpose use (i.e. is at least the lrng_drng_init
+ * fully allocated)?
+ */
+static atomic_t lrng_avail = ATOMIC_INIT(0);
+
+/* Guard protecting all crypto callback update operation of all DRNGs. */
+DEFINE_MUTEX(lrng_crypto_cb_update);
+
+/*
+ * Default hash callback that provides the crypto primitive right from the
+ * kernel start. It must not perform any memory allocation operation, but
+ * simply perform the hash calculation.
+ */
+const struct lrng_hash_cb *lrng_default_hash_cb = &lrng_sha_hash_cb;
+
+/*
+ * Default DRNG callback that provides the crypto primitive which is
+ * allocated either during late kernel boot stage. So, it is permissible for
+ * the callback to perform memory allocation operations.
+ */
+const struct lrng_drng_cb *lrng_default_drng_cb =
+#if defined(CONFIG_LRNG_DFLT_DRNG_CHACHA20)
+ &lrng_cc20_drng_cb;
+#elif defined(CONFIG_LRNG_DFLT_DRNG_DRBG)
+ &lrng_drbg_cb;
+#elif defined(CONFIG_LRNG_DFLT_DRNG_KCAPI)
+ &lrng_kcapi_drng_cb;
+#else
+#error "Unknown default DRNG selected"
+#endif
+
+/* DRNG for non-atomic use cases */
+static struct lrng_drng lrng_drng_init = {
+ LRNG_DRNG_STATE_INIT(lrng_drng_init, NULL, NULL, NULL,
+ &lrng_sha_hash_cb),
+ .lock = __MUTEX_INITIALIZER(lrng_drng_init.lock),
+};
+
+/* Prediction-resistance DRNG: only deliver as much data as received entropy */
+static struct lrng_drng lrng_drng_pr = {
+ LRNG_DRNG_STATE_INIT(lrng_drng_pr, NULL, NULL, NULL,
+ &lrng_sha_hash_cb),
+ .lock = __MUTEX_INITIALIZER(lrng_drng_pr.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
+
+static bool force_seeding = true;
+#ifdef CONFIG_LRNG_RUNTIME_FORCE_SEEDING_DISABLE
+module_param(force_seeding, bool, 0444);
+MODULE_PARM_DESC(force_seeding,
+ "Allow disabling of the forced seeding when insufficient entropy is available\n");
+#endif
+
+/* Wait queue to wait until the LRNG is initialized - can freely be used */
+DECLARE_WAIT_QUEUE_HEAD(lrng_init_wait);
+
+/********************************** Helper ************************************/
+
+bool lrng_get_available(void)
+{
+ return likely(atomic_read(&lrng_avail));
+}
+
+struct lrng_drng *lrng_drng_init_instance(void)
+{
+ return &lrng_drng_init;
+}
+
+struct lrng_drng *lrng_drng_pr_instance(void)
+{
+ return &lrng_drng_pr;
+}
+
+struct lrng_drng *lrng_drng_node_instance(void)
+{
+ struct lrng_drng **lrng_drng = lrng_drng_instances();
+ int node = numa_node_id();
+
+ if (lrng_drng && lrng_drng[node])
+ return lrng_drng[node];
+
+ return lrng_drng_init_instance();
+}
+
+void lrng_drng_reset(struct lrng_drng *drng)
+{
+ /* Ensure reseed during next call */
+ atomic_set(&drng->requests, 1);
+ atomic_set(&drng->requests_since_fully_seeded, 0);
+ drng->last_seeded = jiffies;
+ drng->fully_seeded = false;
+ /* Do not set force, as this flag is used for the emergency reseeding */
+ drng->force_reseed = false;
+ pr_debug("reset DRNG\n");
+}
+
+/* Initialize the DRNG, except the mutex lock */
+int lrng_drng_alloc_common(struct lrng_drng *drng,
+ const struct lrng_drng_cb *drng_cb)
+{
+ if (!drng || !drng_cb)
+ return -EINVAL;
+ if (!IS_ERR_OR_NULL(drng->drng))
+ return 0;
+
+ drng->drng_cb = drng_cb;
+ drng->drng = drng_cb->drng_alloc(LRNG_DRNG_SECURITY_STRENGTH_BYTES);
+ if (IS_ERR(drng->drng))
+ return -PTR_ERR(drng->drng);
+
+ lrng_drng_reset(drng);
+ return 0;
+}
+
+/* Initialize the default DRNG during boot and perform its seeding */
+int lrng_drng_initalize(void)
+{
+ int ret;
+
+ if (lrng_get_available())
+ return 0;
+
+ /* Catch programming error */
+ WARN_ON(lrng_drng_init.hash_cb != lrng_default_hash_cb);
+
+ mutex_lock(&lrng_drng_init.lock);
+ if (lrng_get_available()) {
+ mutex_unlock(&lrng_drng_init.lock);
+ return 0;
+ }
+
+ /* Initialize the PR DRNG inside init lock as it guards lrng_avail. */
+ mutex_lock(&lrng_drng_pr.lock);
+ ret = lrng_drng_alloc_common(&lrng_drng_pr, lrng_default_drng_cb);
+ mutex_unlock(&lrng_drng_pr.lock);
+
+ if (!ret) {
+ ret = lrng_drng_alloc_common(&lrng_drng_init,
+ lrng_default_drng_cb);
+ if (!ret)
+ atomic_set(&lrng_avail, 1);
+ }
+ mutex_unlock(&lrng_drng_init.lock);
+ if (ret)
+ return ret;
+
+ pr_debug("LRNG for general use is available\n");
+
+ /* Seed the DRNG with any entropy available */
+ if (lrng_pool_trylock()) {
+ pr_info("Initial DRNG initialized triggering first seeding\n");
+ lrng_drng_seed_work(NULL);
+ } else {
+ pr_info("Initial DRNG initialized without seeding\n");
+ }
+
+ return 0;
+}
+
+static int __init lrng_drng_make_available(void)
+{
+ return lrng_drng_initalize();
+}
+late_initcall(lrng_drng_make_available);
+
+bool lrng_sp80090c_compliant(void)
+{
+ /* SP800-90C compliant oversampling is only requested in FIPS mode */
+ return fips_enabled;
+}
+
+/************************* Random Number Generation ***************************/
+
+/* Inject a data buffer into the DRNG - caller must hold its lock */
+void lrng_drng_inject(struct lrng_drng *drng, const u8 *inbuf, u32 inbuflen,
+ bool fully_seeded, const char *drng_type)
+{
+ BUILD_BUG_ON(LRNG_DRNG_RESEED_THRESH > INT_MAX);
+ pr_debug("seeding %s DRNG with %u bytes\n", drng_type, inbuflen);
+ if (drng->drng_cb->drng_seed(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("%s DRNG fully seeded\n", drng_type);
+ }
+ }
+}
+
+/*
+ * Perform the seeding of the DRNG with data from entropy source.
+ * The function returns the entropy injected into the DRNG in bits.
+ */
+static u32 lrng_drng_seed_es_nolock(struct lrng_drng *drng, bool init_ops,
+ const char *drng_type)
+{
+ struct entropy_buf seedbuf __aligned(LRNG_KCAPI_ALIGN),
+ collected_seedbuf;
+ u32 collected_entropy = 0;
+ unsigned int i, num_es_delivered = 0;
+ bool forced = drng->force_reseed;
+
+ for_each_lrng_es(i)
+ collected_seedbuf.e_bits[i] = 0;
+
+ do {
+ /* Count the number of ES which delivered entropy */
+ num_es_delivered = 0;
+
+ if (collected_entropy)
+ pr_debug("Force fully seeding level for %s DRNG by repeatedly pull entropy from available entropy sources\n",
+ drng_type);
+
+ lrng_fill_seed_buffer(&seedbuf,
+ lrng_get_seed_entropy_osr(drng->fully_seeded),
+ forced && !drng->fully_seeded);
+
+ collected_entropy += lrng_entropy_rate_eb(&seedbuf);
+
+ /* Sum iterations up. */
+ for_each_lrng_es(i) {
+ collected_seedbuf.e_bits[i] += seedbuf.e_bits[i];
+ num_es_delivered += !!seedbuf.e_bits[i];
+ }
+
+ lrng_drng_inject(drng, (u8 *)&seedbuf, sizeof(seedbuf),
+ lrng_fully_seeded(drng->fully_seeded,
+ collected_entropy,
+ &collected_seedbuf),
+ drng_type);
+
+ /*
+ * Set the seeding state of the LRNG
+ *
+ * Do not call lrng_init_ops(seedbuf) here as the atomic DRNG
+ * does not serve common users.
+ */
+ if (init_ops)
+ lrng_init_ops(&collected_seedbuf);
+
+ /*
+ * Emergency reseeding: If we reached the min seed threshold now
+ * multiple times but never reached fully seeded level and we collect
+ * entropy, keep doing it until we reached fully seeded level for
+ * at least one DRNG. This operation is not continued if the
+ * ES do not deliver entropy such that we cannot reach the fully seeded
+ * level.
+ *
+ * The emergency reseeding implies that the consecutively injected
+ * entropy can be added up. This is applicable due to the fact that
+ * the entire operation is atomic which means that the DRNG is not
+ * producing data while this is ongoing.
+ */
+ } while (force_seeding && forced && !drng->fully_seeded &&
+ num_es_delivered >= (lrng_ntg1_2022_compliant() ? 2 : 1));
+
+ memzero_explicit(&seedbuf, sizeof(seedbuf));
+
+ return collected_entropy;
+}
+
+static void lrng_drng_seed_es(struct lrng_drng *drng)
+{
+ mutex_lock(&drng->lock);
+ lrng_drng_seed_es_nolock(drng, true, "regular");
+ mutex_unlock(&drng->lock);
+}
+
+static void lrng_drng_seed(struct lrng_drng *drng)
+{
+ BUILD_BUG_ON(LRNG_MIN_SEED_ENTROPY_BITS >
+ LRNG_DRNG_SECURITY_STRENGTH_BITS);
+
+ /* (Re-)Seed DRNG */
+ lrng_drng_seed_es(drng);
+ /* (Re-)Seed atomic DRNG from regular DRNG */
+ lrng_drng_atomic_seed_drng(drng);
+}
+
+static void lrng_drng_seed_work_one(struct lrng_drng *drng, u32 node)
+{
+ pr_debug("reseed triggered by system events 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;
+ }
+}
+
+/*
+ * DRNG reseed trigger: Kernel thread handler triggered by the schedule_work()
+ */
+static void __lrng_drng_seed_work(bool force)
+{
+ struct lrng_drng **lrng_drng;
+ u32 node;
+
+ /*
+ * If the DRNG is not yet initialized, let us try to seed the atomic
+ * DRNG.
+ */
+ if (!lrng_get_available()) {
+ struct lrng_drng *atomic;
+ unsigned long flags;
+
+ if (wq_has_sleeper(&lrng_init_wait)) {
+ lrng_init_ops(NULL);
+ return;
+ }
+ atomic = lrng_get_atomic();
+ if (!atomic || atomic->fully_seeded)
+ return;
+
+ atomic->force_reseed |= force;
+ spin_lock_irqsave(&atomic->spin_lock, flags);
+ lrng_drng_seed_es_nolock(atomic, false, "atomic");
+ spin_unlock_irqrestore(&atomic->spin_lock, flags);
+
+ return;
+ }
+
+ lrng_drng = lrng_drng_instances();
+ if (lrng_drng) {
+ for_each_online_node(node) {
+ struct lrng_drng *drng = lrng_drng[node];
+
+ if (drng && !drng->fully_seeded) {
+ drng->force_reseed |= force;
+ lrng_drng_seed_work_one(drng, node);
+ return;
+ }
+ }
+ } else {
+ if (!lrng_drng_init.fully_seeded) {
+ lrng_drng_init.force_reseed |= force;
+ lrng_drng_seed_work_one(&lrng_drng_init, 0);
+ return;
+ }
+ }
+
+ if (!lrng_drng_pr.fully_seeded) {
+ lrng_drng_pr.force_reseed |= force;
+ lrng_drng_seed_work_one(&lrng_drng_pr, 0);
+ return;
+ }
+
+ lrng_pool_all_numa_nodes_seeded(true);
+}
+
+void lrng_drng_seed_work(struct work_struct *dummy)
+{
+ __lrng_drng_seed_work(false);
+
+ /* 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();
+}
+EXPORT_SYMBOL(lrng_drng_force_reseed);
+
+static bool lrng_drng_must_reseed(struct lrng_drng *drng)
+{
+ return (atomic_dec_and_test(&drng->requests) ||
+ drng->force_reseed ||
+ time_after(jiffies,
+ drng->last_seeded + lrng_drng_reseed_max_time * HZ));
+}
+
+/*
+ * lrng_drng_get() - Get random data out of the DRNG which is reseeded
+ * frequently.
+ *
+ * @drng: DRNG instance
+ * @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
+ */
+int lrng_drng_get(struct lrng_drng *drng, u8 *outbuf, u32 outbuflen)
+{
+ u32 processed = 0;
+ bool pr = (drng == &lrng_drng_pr) ? true : false;
+
+ if (!outbuf || !outbuflen)
+ return 0;
+
+ if (!lrng_get_available())
+ return -EOPNOTSUPP;
+
+ outbuflen = min_t(size_t, outbuflen, INT_MAX);
+
+ /* 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;
+
+ /* In normal operation, check whether to reseed */
+ if (!pr && lrng_drng_must_reseed(drng)) {
+ if (!lrng_pool_trylock()) {
+ drng->force_reseed = true;
+ } else {
+ lrng_drng_seed(drng);
+ lrng_pool_unlock();
+ }
+ }
+
+ mutex_lock(&drng->lock);
+
+ if (pr) {
+ /* If async reseed did not deliver entropy, try now */
+ if (!drng->fully_seeded) {
+ u32 coll_ent_bits;
+
+ /* If we cannot get the pool lock, try again. */
+ if (!lrng_pool_trylock()) {
+ mutex_unlock(&drng->lock);
+ continue;
+ }
+
+ coll_ent_bits = lrng_drng_seed_es_nolock(
+ drng, true, "regular");
+
+ lrng_pool_unlock();
+
+ /* If no new entropy was received, stop now. */
+ if (!coll_ent_bits) {
+ mutex_unlock(&drng->lock);
+ goto out;
+ }
+
+ /* Produce no more data than received entropy */
+ todo = min_t(u32, todo, coll_ent_bits >> 3);
+ }
+
+ /* Do not produce more than DRNG security strength */
+ todo = min_t(u32, todo, lrng_security_strength() >> 3);
+ }
+ ret = drng->drng_cb->drng_generate(drng->drng,
+ outbuf + processed, todo);
+
+ mutex_unlock(&drng->lock);
+ if (ret <= 0) {
+ pr_warn("getting random data from DRNG failed (%d)\n",
+ ret);
+ return -EFAULT;
+ }
+ processed += ret;
+ outbuflen -= ret;
+
+ if (pr) {
+ /* Force the async reseed for PR DRNG */
+ lrng_unset_fully_seeded(drng);
+ if (outbuflen)
+ cond_resched();
+ }
+ }
+
+out:
+ return processed;
+}
+
+int lrng_drng_get_sleep(u8 *outbuf, u32 outbuflen, bool pr)
+{
+ struct lrng_drng **lrng_drng = lrng_drng_instances();
+ struct lrng_drng *drng = &lrng_drng_init;
+ int ret, node = numa_node_id();
+
+ might_sleep();
+
+ if (pr)
+ drng = &lrng_drng_pr;
+ else if (lrng_drng && lrng_drng[node] && lrng_drng[node]->fully_seeded)
+ drng = lrng_drng[node];
+
+ ret = lrng_drng_initalize();
+ if (ret)
+ return ret;
+
+ 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();
+
+ if (!lrng_drng) {
+ mutex_lock(&lrng_drng_init.lock);
+ lrng_drng_reset(&lrng_drng_init);
+ mutex_unlock(&lrng_drng_init.lock);
+ } else {
+ u32 node;
+
+ for_each_online_node(node) {
+ struct lrng_drng *drng = lrng_drng[node];
+
+ if (!drng)
+ continue;
+ mutex_lock(&drng->lock);
+ lrng_drng_reset(drng);
+ mutex_unlock(&drng->lock);
+ }
+ }
+
+ mutex_lock(&lrng_drng_pr.lock);
+ lrng_drng_reset(&lrng_drng_pr);
+ mutex_unlock(&lrng_drng_pr.lock);
+
+ lrng_drng_atomic_reset();
+ 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);
+}
+
+/******************* Generic LRNG kernel output interfaces ********************/
+
+void lrng_force_fully_seeded(void)
+{
+ if (lrng_pool_all_numa_nodes_seeded_get())
+ return;
+
+ lrng_pool_lock();
+ __lrng_drng_seed_work(true);
+ lrng_pool_unlock();
+}
+
+static int lrng_drng_sleep_while_not_all_nodes_seeded(unsigned int nonblock)
+{
+ lrng_force_fully_seeded();
+ if (lrng_pool_all_numa_nodes_seeded_get())
+ return 0;
+ if (nonblock)
+ return -EAGAIN;
+ wait_event_interruptible(lrng_init_wait,
+ lrng_pool_all_numa_nodes_seeded_get());
+ return 0;
+}
+
+int lrng_drng_sleep_while_nonoperational(int nonblock)
+{
+ lrng_force_fully_seeded();
+ if (likely(lrng_state_operational()))
+ return 0;
+ if (nonblock)
+ return -EAGAIN;
+ return wait_event_interruptible(lrng_init_wait,
+ lrng_state_operational());
+}
+
+int lrng_drng_sleep_while_non_min_seeded(void)
+{
+ lrng_force_fully_seeded();
+ if (likely(lrng_state_min_seeded()))
+ return 0;
+ return wait_event_interruptible(lrng_init_wait,
+ lrng_state_min_seeded());
+}
+
+ssize_t lrng_get_seed(u64 *buf, size_t nbytes, unsigned int flags)
+{
+ struct entropy_buf *eb = (struct entropy_buf *)(buf + 2);
+ u64 buflen = sizeof(struct entropy_buf) + 2 * sizeof(u64);
+ u64 collected_bits = 0;
+ int ret;
+
+ /* Ensure buffer is aligned as required */
+ BUILD_BUG_ON(sizeof(buflen) > LRNG_KCAPI_ALIGN);
+ if (nbytes < sizeof(buflen))
+ return -EINVAL;
+
+ /* Write buffer size into first word */
+ buf[0] = buflen;
+ if (nbytes < buflen)
+ return -EMSGSIZE;
+
+ ret = lrng_drng_sleep_while_not_all_nodes_seeded(
+ flags & LRNG_GET_SEED_NONBLOCK);
+ if (ret)
+ return ret;
+
+ /* Try to get the pool lock and sleep on it to get it. */
+ lrng_pool_lock();
+
+ /* If an LRNG DRNG becomes unseeded, give this DRNG precedence. */
+ if (!lrng_pool_all_numa_nodes_seeded_get()) {
+ lrng_pool_unlock();
+ return 0;
+ }
+
+ /*
+ * Try to get seed data - a rarely used busyloop is cheaper than a wait
+ * queue that is constantly woken up by the hot code path of
+ * lrng_init_ops.
+ */
+ for (;;) {
+ lrng_fill_seed_buffer(eb,
+ lrng_get_seed_entropy_osr(flags &
+ LRNG_GET_SEED_FULLY_SEEDED),
+ false);
+ collected_bits = lrng_entropy_rate_eb(eb);
+
+ /* Break the collection loop if we got entropy, ... */
+ if (collected_bits ||
+ /* ... a DRNG becomes unseeded, give DRNG precedence, ... */
+ !lrng_pool_all_numa_nodes_seeded_get() ||
+ /* ... if the caller does not want a blocking behavior. */
+ (flags & LRNG_GET_SEED_NONBLOCK))
+ break;
+
+ schedule();
+ }
+
+ lrng_pool_unlock();
+
+ /* Write collected entropy size into second word */
+ buf[1] = collected_bits;
+
+ return (ssize_t)buflen;
+}
+
+void lrng_get_random_bytes_full(void *buf, int nbytes)
+{
+ lrng_drng_sleep_while_nonoperational(0);
+ lrng_drng_get_sleep((u8 *)buf, (u32)nbytes, false);
+}
+EXPORT_SYMBOL(lrng_get_random_bytes_full);
+
+void lrng_get_random_bytes_min(void *buf, int nbytes)
+{
+ lrng_drng_sleep_while_non_min_seeded();
+ lrng_drng_get_sleep((u8 *)buf, (u32)nbytes, false);
+}
+EXPORT_SYMBOL(lrng_get_random_bytes_min);
+
+int lrng_get_random_bytes_pr(void *buf, int nbytes)
+{
+ lrng_drng_sleep_while_nonoperational(0);
+ return lrng_drng_get_sleep((u8 *)buf, (u32)nbytes, true);
+}
+EXPORT_SYMBOL(lrng_get_random_bytes_pr);
--- /dev/null
+++ b/drivers/char/lrng/lrng_drng_mgr.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_DRNG_H
+#define _LRNG_DRNG_H
+
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+#include "lrng_definitions.h"
+
+extern struct wait_queue_head lrng_init_wait;
+extern int lrng_drng_reseed_max_time;
+extern struct mutex lrng_crypto_cb_update;
+extern const struct lrng_drng_cb *lrng_default_drng_cb;
+extern const struct lrng_hash_cb *lrng_default_hash_cb;
+
+/* DRNG state handle */
+struct lrng_drng {
+ void *drng; /* DRNG handle */
+ void *hash; /* Hash handle */
+ const struct lrng_drng_cb *drng_cb; /* DRNG callbacks */
+ const struct lrng_hash_cb *hash_cb; /* Hash 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 */
+
+ rwlock_t hash_lock; /* Lock hash_cb replacement */
+ /* Lock write operations on DRNG state, DRNG replacement of drng_cb */
+ struct mutex lock; /* Non-atomic DRNG operation */
+ spinlock_t spin_lock; /* Atomic DRNG operation */
+};
+
+#define LRNG_DRNG_STATE_INIT(x, d, h, d_cb, h_cb) \
+ .drng = d, \
+ .hash = h, \
+ .drng_cb = d_cb, \
+ .hash_cb = h_cb, \
+ .requests = ATOMIC_INIT(LRNG_DRNG_RESEED_THRESH),\
+ .requests_since_fully_seeded = ATOMIC_INIT(0), \
+ .last_seeded = 0, \
+ .fully_seeded = false, \
+ .force_reseed = true, \
+ .hash_lock = __RW_LOCK_UNLOCKED(x.hash_lock)
+
+struct lrng_drng *lrng_drng_init_instance(void);
+struct lrng_drng *lrng_drng_pr_instance(void);
+struct lrng_drng *lrng_drng_node_instance(void);
+
+void lrng_reset(void);
+int lrng_drng_alloc_common(struct lrng_drng *drng,
+ const struct lrng_drng_cb *crypto_cb);
+int lrng_drng_initalize(void);
+bool lrng_sp80090c_compliant(void);
+bool lrng_get_available(void);
+void lrng_drng_reset(struct lrng_drng *drng);
+void lrng_drng_inject(struct lrng_drng *drng, const u8 *inbuf, u32 inbuflen,
+ bool fully_seeded, const char *drng_type);
+int lrng_drng_get(struct lrng_drng *drng, u8 *outbuf, u32 outbuflen);
+int lrng_drng_sleep_while_nonoperational(int nonblock);
+int lrng_drng_sleep_while_non_min_seeded(void);
+int lrng_drng_get_sleep(u8 *outbuf, u32 outbuflen, bool pr);
+void lrng_drng_seed_work(struct work_struct *dummy);
+void lrng_drng_force_reseed(void);
+void lrng_force_fully_seeded(void);
+
+static inline u32 lrng_compress_osr(void)
+{
+ return lrng_sp80090c_compliant() ? 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;
+}
+
+#endif /* _LRNG_DRNG_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_aux.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG Slow Entropy Source: Auxiliary entropy pool
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/lrng.h>
+
+#include "lrng_es_aux.h"
+#include "lrng_es_mgr.h"
+#include "lrng_sysctl.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 */
+static u32 lrng_aux_avail_entropy(u32 __unused)
+{
+ /* 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 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 write wakeup threshold which must not be larger
+ * than the digest size of the current conditioning hash.
+ */
+ digestsize = lrng_reduce_by_osr(digestsize << 3);
+ lrng_sysctl_update_max_write_thresh(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);
+}
+
+static int __init lrng_init_wakeup_bits(void)
+{
+ u32 digestsize = lrng_reduce_by_osr(lrng_get_digestsize());
+
+ lrng_sysctl_update_max_write_thresh(digestsize);
+ lrng_write_wakeup_bits = digestsize;
+ return 0;
+}
+core_initcall(lrng_init_wakeup_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);
+}
+
+static void lrng_aux_reset(void)
+{
+ lrng_pool_set_entropy(0);
+}
+
+/*
+ * 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).
+ */
+static int
+lrng_aux_switch_hash(struct lrng_drng *drng, int __unused,
+ const struct lrng_hash_cb *new_cb, void *new_hash,
+ const struct lrng_hash_cb *old_cb)
+{
+ struct lrng_drng *init_drng = lrng_drng_init_instance();
+ 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_SWITCH))
+ return -EOPNOTSUPP;
+
+ if (unlikely(!pool->initialized))
+ return 0;
+
+ /* We only switch if the processed DRNG is the initial DRNG. */
+ if (init_drng != drng)
+ return 0;
+
+ /* Get the aux pool hash with old digest ... */
+ ret = old_cb->hash_final(shash, digest) ?:
+ /* ... re-initialize the hash with the new digest ... */
+ new_cb->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->hash_update(shash, digest, sizeof(digest));
+ if (!ret) {
+ lrng_set_digestsize(new_cb->hash_digestsize(new_hash));
+ pr_debug("Re-initialize aux entropy pool with hash %s\n",
+ new_cb->hash_name());
+ }
+
+ memzero_explicit(digest, sizeof(digest));
+ return ret;
+}
+
+/* Insert data into auxiliary pool by using the hash update function. */
+static int
+lrng_aux_pool_insert_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_hash_cb *hash_cb;
+ unsigned long flags;
+ void *hash;
+ int ret;
+
+ entropy_bits = min_t(u32, entropy_bits, inbuflen << 3);
+
+ read_lock_irqsave(&drng->hash_lock, flags);
+ hash_cb = drng->hash_cb;
+ hash = drng->hash;
+
+ if (unlikely(!pool->initialized)) {
+ ret = hash_cb->hash_init(shash, hash);
+ if (ret)
+ goto out;
+ pool->initialized = true;
+ }
+
+ ret = hash_cb->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,
+ hash_cb->hash_digestsize(hash) << 3));
+
+out:
+ read_unlock_irqrestore(&drng->hash_lock, 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_aux_pool_insert_locked(inbuf, inbuflen, entropy_bits);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ lrng_es_add_entropy();
+
+ return ret;
+}
+EXPORT_SYMBOL(lrng_pool_insert_aux);
+
+/************************* 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 u32 lrng_aux_get_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_hash_cb *hash_cb;
+ unsigned long flags;
+ void *hash;
+ u32 collected_ent_bits, returned_ent_bits, unused_bits = 0,
+ digestsize, digestsize_bits, requested_bits_osr;
+ u8 aux_output[LRNG_MAX_DIGESTSIZE];
+
+ if (unlikely(!pool->initialized))
+ return 0;
+
+ read_lock_irqsave(&drng->hash_lock, flags);
+
+ hash_cb = drng->hash_cb;
+ hash = drng->hash;
+ digestsize = hash_cb->hash_digestsize(hash);
+ digestsize_bits = digestsize << 3;
+
+ /* Cap to maximum entropy that can ever be generated with given hash */
+ lrng_cap_requested(digestsize_bits, requested_bits);
+
+ /* Ensure that no more than the size of aux_pool can be requested */
+ requested_bits = min_t(u32, requested_bits, (LRNG_MAX_DIGESTSIZE << 3));
+ requested_bits_osr = requested_bits + lrng_compress_osr();
+
+ /* Cap entropy with entropy counter from aux pool and the used digest */
+ collected_ent_bits = min_t(u32, digestsize_bits,
+ atomic_xchg_relaxed(&pool->aux_entropy_bits, 0));
+
+ /* We collected too much entropy and put the overflow back */
+ if (collected_ent_bits > requested_bits_osr) {
+ /* Amount of bits we collected too much */
+ unused_bits = collected_ent_bits - requested_bits_osr;
+ /* Put entropy back */
+ atomic_add(unused_bits, &pool->aux_entropy_bits);
+ /* Fix collected entropy */
+ collected_ent_bits = requested_bits_osr;
+ }
+
+ /* 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 (hash_cb->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.
+ */
+ hash_cb->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);
+ }
+
+ read_unlock_irqrestore(&drng->hash_lock, flags);
+ memzero_explicit(aux_output, digestsize);
+ return returned_ent_bits;
+}
+
+static void lrng_aux_get_backtrack(struct entropy_buf *eb, u32 requested_bits,
+ bool __unused)
+{
+ struct lrng_pool *pool = &lrng_pool;
+ unsigned long flags;
+
+ /* Ensure aux pool extraction and backtracking op are atomic */
+ spin_lock_irqsave(&pool->lock, flags);
+
+ eb->e_bits[lrng_ext_es_aux] = lrng_aux_get_pool(eb->e[lrng_ext_es_aux],
+ requested_bits);
+
+ /* Mix the extracted data back into pool for backtracking resistance */
+ if (lrng_aux_pool_insert_locked((u8 *)eb,
+ sizeof(struct entropy_buf), 0))
+ pr_warn("Backtracking resistance operation failed\n");
+
+ spin_unlock_irqrestore(&pool->lock, flags);
+}
+
+static 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,
+ " Hash for operating entropy pool: %s\n"
+ " Available entropy: %u\n",
+ lrng_drng_init->hash_cb->hash_name(),
+ lrng_aux_avail_entropy(0));
+}
+
+struct lrng_es_cb lrng_es_aux = {
+ .name = "Auxiliary",
+ .get_ent = lrng_aux_get_backtrack,
+ .curr_entropy = lrng_aux_avail_entropy,
+ .max_entropy = lrng_get_digestsize,
+ .state = lrng_aux_es_state,
+ .reset = lrng_aux_reset,
+ .switch_hash = lrng_aux_switch_hash,
+};
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_aux.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_ES_AUX_H
+#define _LRNG_ES_AUX_H
+
+#include "lrng_drng_mgr.h"
+#include "lrng_es_mgr_cb.h"
+
+u32 lrng_get_digestsize(void);
+void lrng_pool_set_entropy(u32 entropy_bits);
+int lrng_pool_insert_aux(const u8 *inbuf, u32 inbuflen, u32 entropy_bits);
+
+extern struct lrng_es_cb lrng_es_aux;
+
+/****************************** Helper code ***********************************/
+
+/* 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 += LRNG_SEED_BUFFER_INIT_ADD_BITS;
+ return requested_bits;
+}
+
+#endif /* _LRNG_ES_AUX_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_cpu.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_ES_CPU_H
+#define _LRNG_ES_CPU_H
+
+#include "lrng_es_mgr_cb.h"
+
+#ifdef CONFIG_LRNG_CPU
+
+extern struct lrng_es_cb lrng_es_cpu;
+
+#endif /* CONFIG_LRNG_CPU */
+
+#endif /* _LRNG_ES_CPU_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_irq.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_ES_IRQ_H
+#define _LRNG_ES_IRQ_H
+
+#include <linux/lrng.h>
+
+#include "lrng_es_mgr_cb.h"
+
+#ifdef CONFIG_LRNG_IRQ
+void lrng_irq_es_init(bool highres_timer);
+void lrng_irq_array_add_u32(u32 data);
+
+extern struct lrng_es_cb lrng_es_irq;
+
+#else /* CONFIG_LRNG_IRQ */
+static inline void lrng_irq_es_init(bool highres_timer) { }
+static inline void lrng_irq_array_add_u32(u32 data) { }
+#endif /* CONFIG_LRNG_IRQ */
+
+#endif /* _LRNG_ES_IRQ_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_jent.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_ES_JENT_H
+#define _LRNG_ES_JENT_H
+
+#include "lrng_es_mgr_cb.h"
+
+#ifdef CONFIG_LRNG_JENT
+
+extern struct lrng_es_cb lrng_es_jent;
+
+#endif /* CONFIG_LRNG_JENT */
+
+#endif /* _LRNG_ES_JENT_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_krng.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_ES_RANDOM_H
+#define _LRNG_ES_RANDOM_H
+
+#include "lrng_es_mgr_cb.h"
+
+#ifdef CONFIG_LRNG_KERNEL_RNG
+
+extern struct lrng_es_cb lrng_es_krng;
+
+#endif /* CONFIG_LRNG_KERNEL_RNG */
+
+#endif /* _LRNG_ES_RANDOM_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_mgr.c
@@ -0,0 +1,506 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG Entropy sources management
+ *
+ * Copyright (C) 2022 - 2023, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/utsname.h>
+#include <linux/workqueue.h>
+#include <asm/archrandom.h>
+
+#include "lrng_drng_atomic.h"
+#include "lrng_drng_mgr.h"
+#include "lrng_es_aux.h"
+#include "lrng_es_cpu.h"
+#include "lrng_es_irq.h"
+#include "lrng_es_jent.h"
+#include "lrng_es_krng.h"
+#include "lrng_es_mgr.h"
+#include "lrng_es_sched.h"
+#include "lrng_interface_dev_common.h"
+#include "lrng_interface_random_kernel.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.
+ */
+
+ atomic_t boot_entropy_thresh; /* Reseed threshold */
+ struct mutex 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,
+ .boot_entropy_thresh = ATOMIC_INIT(LRNG_INIT_ENTROPY_BITS),
+ .reseed_in_progress =
+ __MUTEX_INITIALIZER(lrng_state.reseed_in_progress),
+};
+
+/*
+ * 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);
+
+/*
+ * The entries must be in the same order as defined by enum lrng_internal_es and
+ * enum lrng_external_es
+ */
+struct lrng_es_cb *lrng_es[] = {
+#ifdef CONFIG_LRNG_IRQ
+ &lrng_es_irq,
+#endif
+#ifdef CONFIG_LRNG_SCHED
+ &lrng_es_sched,
+#endif
+#ifdef CONFIG_LRNG_JENT
+ &lrng_es_jent,
+#endif
+#ifdef CONFIG_LRNG_CPU
+ &lrng_es_cpu,
+#endif
+#ifdef CONFIG_LRNG_KERNEL_RNG
+ &lrng_es_krng,
+#endif
+ &lrng_es_aux
+};
+
+static bool ntg1 = false;
+#ifdef CONFIG_LRNG_AIS2031_NTG1_SEEDING_STRATEGY
+module_param(ntg1, bool, 0444);
+MODULE_PARM_DESC(ntg1, "Enable AIS20/31 NTG.1 compliant seeding strategy\n");
+#endif
+
+/* Only panic the kernel on permanent health failure if this variable is true */
+static bool lrng_panic_on_permanent_health_failure = false;
+module_param(lrng_panic_on_permanent_health_failure, bool, 0444);
+MODULE_PARM_DESC(lrng_panic_on_permanent_health_failure, "Panic on reaching permanent health failure - only required if LRNG is part of a FIPS 140-3 module\n");
+
+/********************************** Helper ***********************************/
+
+bool lrng_enforce_panic_on_permanent_health_failure(void)
+{
+ return lrng_panic_on_permanent_health_failure;
+}
+
+bool lrng_ntg1_2022_compliant(void)
+{
+ /* Implies use of /dev/random w/ O_SYNC / getrandom w/ GRND_RANDOM */
+ return ntg1;
+}
+
+void lrng_debug_report_seedlevel(const char *name)
+{
+#ifdef CONFIG_WARN_ALL_UNSEEDED_RANDOM
+ static void *previous = NULL;
+ void *caller = (void *) _RET_IP_;
+ struct lrng_drng *atomic = lrng_get_atomic();
+
+ if (READ_ONCE(previous) == caller)
+ return;
+
+ if (atomic && !atomic->fully_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
+}
+
+/*
+ * 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 mutex_trylock(&lrng_state.reseed_in_progress);
+}
+
+void lrng_pool_lock(void)
+{
+ mutex_lock(&lrng_state.reseed_in_progress);
+}
+
+void lrng_pool_unlock(void)
+{
+ mutex_unlock(&lrng_state.reseed_in_progress);
+}
+
+/* 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)
+{
+ u32 i;
+
+ for_each_lrng_es(i) {
+ if (lrng_es[i]->reset)
+ lrng_es[i]->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;
+ if (set)
+ wake_up_all(&lrng_init_wait);
+}
+
+bool lrng_pool_all_numa_nodes_seeded_get(void)
+{
+ return lrng_state.all_online_numa_node_seeded;
+}
+
+/* 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;
+}
+
+static void lrng_init_wakeup(void)
+{
+ wake_up_all(&lrng_init_wait);
+ lrng_init_wakeup_dev();
+ lrng_kick_random_ready();
+}
+
+static u32 lrng_avail_entropy_thresh(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 += LRNG_SEED_BUFFER_INIT_ADD_BITS;
+
+ return ent_thresh;
+}
+
+bool lrng_fully_seeded(bool fully_seeded, u32 collected_entropy,
+ struct entropy_buf *eb)
+{
+ /* AIS20/31 NTG.1: two entropy sources with each delivering 220 bits */
+ if (ntg1) {
+ u32 i, result = 0, ent_thresh = lrng_avail_entropy_thresh();
+
+ for_each_lrng_es(i) {
+ result += (eb ? eb->e_bits[i] :
+ lrng_es[i]->curr_entropy(ent_thresh)) >=
+ LRNG_AIS2031_NPTRNG_MIN_ENTROPY;
+ }
+
+ return (result >= 2);
+ }
+
+ return (collected_entropy >= lrng_get_seed_entropy_osr(fully_seeded));
+}
+
+u32 lrng_entropy_rate_eb(struct entropy_buf *eb)
+{
+ u32 i, collected_entropy = 0;
+
+ for_each_lrng_es(i)
+ collected_entropy += eb->e_bits[i];
+
+ return collected_entropy;
+}
+
+/* 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_es_add_entropy();
+ }
+}
+
+/* Policy to enable LRNG operational mode */
+static void lrng_set_operational(void)
+{
+ /*
+ * LRNG is operational if the initial DRNG is fully seeded. This state
+ * can only occur if either the external entropy sources provided
+ * sufficient entropy, or the SP800-90B startup test completed for
+ * the internal ES to supply also entropy data.
+ */
+ if (lrng_state.lrng_fully_seeded) {
+ lrng_state.lrng_operational = true;
+ 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 i, ent = 0, ent_thresh = lrng_avail_entropy_thresh();
+
+ BUILD_BUG_ON(ARRAY_SIZE(lrng_es) != lrng_ext_es_last);
+ for_each_lrng_es(i)
+ ent += lrng_es[i]->curr_entropy(ent_thresh);
+ return ent;
+}
+
+u32 lrng_avail_entropy_aux(void)
+{
+ u32 ent_thresh = lrng_avail_entropy_thresh();
+
+ return lrng_es[lrng_ext_es_aux]->curr_entropy(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 - if
+ * NULL, the function obtains the available entropy from the ES.
+ */
+void lrng_init_ops(struct entropy_buf *eb)
+{
+ struct lrng_state *state = &lrng_state;
+ u32 i, requested_bits, seed_bits = 0;
+
+ if (state->lrng_operational)
+ return;
+
+ requested_bits = ntg1 ?
+ /* Approximation so that two ES should deliver 220 bits each */
+ (lrng_avail_entropy() + LRNG_AIS2031_NPTRNG_MIN_ENTROPY) :
+ /* Apply SP800-90C oversampling if applicable */
+ lrng_get_seed_entropy_osr(state->all_online_numa_node_seeded);
+
+ if (eb) {
+ seed_bits = lrng_entropy_rate_eb(eb);
+ } else {
+ u32 ent_thresh = lrng_avail_entropy_thresh();
+
+ for_each_lrng_es(i)
+ seed_bits += lrng_es[i]->curr_entropy(ent_thresh);
+ }
+
+ /* DRNG is seeded with full security strength */
+ if (state->lrng_fully_seeded) {
+ lrng_set_operational();
+ lrng_set_entropy_thresh(requested_bits);
+ } else if (lrng_fully_seeded(state->all_online_numa_node_seeded,
+ seed_bits, eb)) {
+ if (state->can_invalidate)
+ invalidate_batched_entropy();
+
+ state->lrng_fully_seeded = true;
+ lrng_set_operational();
+ 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);
+ }
+ }
+}
+
+void __init lrng_rand_initialize_early(void)
+{
+ struct seed {
+ unsigned long data[((LRNG_MAX_DIGESTSIZE +
+ sizeof(unsigned long) - 1) /
+ sizeof(unsigned long))];
+ struct new_utsname utsname;
+ } seed __aligned(LRNG_KCAPI_ALIGN);
+ size_t longs = 0;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(seed.data); i += longs) {
+ longs = arch_get_random_seed_longs(seed.data + i,
+ ARRAY_SIZE(seed.data) - i);
+ if (longs)
+ continue;
+ longs = arch_get_random_longs(seed.data + i,
+ ARRAY_SIZE(seed.data) - i);
+ if (longs)
+ continue;
+ longs = 1;
+ }
+ memcpy(&seed.utsname, init_utsname(), sizeof(*(init_utsname())));
+
+ lrng_pool_insert_aux((u8 *)&seed, sizeof(seed), 0);
+ memzero_explicit(&seed, sizeof(seed));
+
+ lrng_force_fully_seeded();
+}
+
+void __init lrng_rand_initialize(void)
+{
+ unsigned long entropy = random_get_entropy();
+ ktime_t time = ktime_get_real();
+
+ lrng_pool_insert_aux((u8 *)&entropy, sizeof(entropy), 0);
+ lrng_pool_insert_aux((u8 *)&time, sizeof(time), 0);
+
+ /* Initialize the seed work queue */
+ INIT_WORK(&lrng_state.lrng_seed_work, lrng_drng_seed_work);
+ lrng_state.perform_seedwork = true;
+
+ invalidate_batched_entropy();
+
+ lrng_state.can_invalidate = true;
+}
+
+#ifndef CONFIG_LRNG_RANDOM_IF
+static int __init lrng_rand_initialize_call(void)
+{
+ lrng_rand_initialize_early();
+ lrng_rand_initialize();
+ return 0;
+}
+
+early_initcall(lrng_rand_initialize_call);
+#endif
+
+/* Interface requesting a reseed of the DRNG */
+void lrng_es_add_entropy(void)
+{
+ /*
+ * Once all DRNGs are fully seeded, the system-triggered arrival of
+ * entropy will not cause any reseeding any more.
+ */
+ if (likely(lrng_state.all_online_numa_node_seeded))
+ 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 *eb, u32 requested_bits,
+ bool force)
+{
+ struct lrng_state *state = &lrng_state;
+ u32 i, 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 */
+ eb->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 (!force &&
+ state->lrng_fully_seeded && (lrng_avail_entropy() < req_ent)) {
+ for_each_lrng_es(i)
+ eb->e_bits[i] = 0;
+
+ goto wakeup;
+ }
+
+ /* Concatenate the output of the entropy sources. */
+ for_each_lrng_es(i) {
+ lrng_es[i]->get_ent(eb, requested_bits,
+ state->lrng_fully_seeded);
+ }
+
+ /* allow external entropy provider to provide seed */
+ lrng_state_exseed_allow_all();
+
+wakeup:
+ lrng_writer_wakeup();
+}
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_mgr.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_ES_MGR_H
+#define _LRNG_ES_MGR_H
+
+#include "lrng_es_mgr_cb.h"
+
+/*************************** General LRNG parameter ***************************/
+
+#define LRNG_DRNG_BLOCKSIZE 64 /* Maximum of DRNG block sizes */
+
+/* Helper to concatenate a macro with an integer type */
+#define LRNG_PASTER(x, y) x ## y
+#define LRNG_UINT32_C(x) LRNG_PASTER(x, U)
+
+/************************* Entropy sources management *************************/
+
+extern struct lrng_es_cb *lrng_es[];
+
+#define for_each_lrng_es(ctr) \
+ for ((ctr) = 0; (ctr) < lrng_ext_es_last; (ctr)++)
+
+bool lrng_enforce_panic_on_permanent_health_failure(void);
+bool lrng_ntg1_2022_compliant(void);
+bool lrng_pool_all_numa_nodes_seeded_get(void);
+bool lrng_state_min_seeded(void);
+void lrng_debug_report_seedlevel(const char *name);
+void lrng_rand_initialize_early(void);
+void lrng_rand_initialize(void);
+bool lrng_state_operational(void);
+
+extern u32 lrng_write_wakeup_bits;
+void lrng_set_entropy_thresh(u32 new);
+u32 lrng_avail_entropy(void);
+u32 lrng_avail_entropy_aux(void);
+void lrng_reset_state(void);
+
+bool lrng_state_fully_seeded(void);
+
+int lrng_pool_trylock(void);
+void lrng_pool_lock(void);
+void lrng_pool_unlock(void);
+void lrng_pool_all_numa_nodes_seeded(bool set);
+
+bool lrng_fully_seeded(bool fully_seeded, u32 collected_entropy,
+ struct entropy_buf *eb);
+u32 lrng_entropy_rate_eb(struct entropy_buf *eb);
+void lrng_unset_fully_seeded(struct lrng_drng *drng);
+void lrng_fill_seed_buffer(struct entropy_buf *eb, u32 requested_bits,
+ bool force);
+void lrng_init_ops(struct entropy_buf *eb);
+
+#endif /* _LRNG_ES_MGR_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_mgr_cb.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ *
+ * Definition of an entropy source.
+ */
+
+#ifndef _LRNG_ES_MGR_CB_H
+#define _LRNG_ES_MGR_CB_H
+
+#include <linux/lrng.h>
+
+#include "lrng_definitions.h"
+#include "lrng_drng_mgr.h"
+
+enum lrng_internal_es {
+#ifdef CONFIG_LRNG_IRQ
+ lrng_int_es_irq, /* IRQ-based entropy source */
+#endif
+#ifdef CONFIG_LRNG_SCHED
+ lrng_int_es_sched, /* Scheduler entropy source */
+#endif
+ lrng_int_es_last, /* MUST be the last entry */
+};
+
+enum lrng_external_es {
+ lrng_ext_link = lrng_int_es_last - 1, /* Link entry */
+#ifdef CONFIG_LRNG_JENT
+ lrng_ext_es_jitter, /* Jitter RNG */
+#endif
+#ifdef CONFIG_LRNG_CPU
+ lrng_ext_es_cpu, /* CPU-based, e.g. RDSEED */
+#endif
+#ifdef CONFIG_LRNG_KERNEL_RNG
+ lrng_ext_es_krng, /* random.c */
+#endif
+ lrng_ext_es_aux, /* MUST BE LAST ES! */
+ lrng_ext_es_last /* MUST be the last entry */
+};
+
+struct entropy_buf {
+ u8 e[lrng_ext_es_last][LRNG_DRNG_INIT_SEED_SIZE_BYTES];
+ u32 now, e_bits[lrng_ext_es_last];
+};
+
+/*
+ * struct lrng_es_cb - callback defining an entropy source
+ * @name: Name of the entropy source.
+ * @get_ent: Fetch entropy into the entropy_buf. The ES shall only deliver
+ * data if its internal initialization is complete, including any
+ * SP800-90B startup testing or similar.
+ * @curr_entropy: Return amount of currently available entropy.
+ * @max_entropy: Maximum amount of entropy the entropy source is able to
+ * maintain.
+ * @state: Buffer with human-readable ES state.
+ * @reset: Reset entropy source (drop all entropy and reinitialize).
+ * This callback may be NULL.
+ * @switch_hash: callback to switch from an old hash callback definition to
+ * a new one. This callback may be NULL.
+ */
+struct lrng_es_cb {
+ const char *name;
+ void (*get_ent)(struct entropy_buf *eb, u32 requested_bits,
+ bool fully_seeded);
+ u32 (*curr_entropy)(u32 requested_bits);
+ u32 (*max_entropy)(void);
+ void (*state)(unsigned char *buf, size_t buflen);
+ void (*reset)(void);
+ int (*switch_hash)(struct lrng_drng *drng, int node,
+ const struct lrng_hash_cb *new_cb, void *new_hash,
+ const struct lrng_hash_cb *old_cb);
+};
+
+/* Allow entropy sources to tell the ES manager that new entropy is there */
+void lrng_es_add_entropy(void);
+
+/* Cap to maximum entropy that can ever be generated with given hash */
+#define lrng_cap_requested(__digestsize_bits, __requested_bits) \
+ do { \
+ if (__digestsize_bits < __requested_bits) { \
+ pr_debug("Cannot satisfy requested entropy %u due to insufficient hash size %u\n",\
+ __requested_bits, __digestsize_bits); \
+ __requested_bits = __digestsize_bits; \
+ } \
+ } while (0)
+
+#endif /* _LRNG_ES_MGR_CB_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_sched.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_ES_SCHED_H
+#define _LRNG_ES_SCHED_H
+
+#include "lrng_es_mgr_cb.h"
+
+#ifdef CONFIG_LRNG_SCHED
+void lrng_sched_es_init(bool highres_timer);
+
+extern struct lrng_es_cb lrng_es_sched;
+
+#else /* CONFIG_LRNG_SCHED */
+static inline void lrng_sched_es_init(bool highres_timer) { }
+#endif /* CONFIG_LRNG_SCHED */
+
+#endif /* _LRNG_ES_SCHED_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_timer_common.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * LRNG Slow Noise Source: Time stamp array handling
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_ES_TIMER_COMMON_H
+#define _LRNG_ES_TIMER_COMMON_H
+
+bool lrng_gcd_tested(void);
+void lrng_gcd_set(u32 running_gcd);
+u32 lrng_gcd_get(void);
+u32 lrng_gcd_analyze(u32 *history, size_t nelem);
+void lrng_gcd_add_value(u32 time);
+bool lrng_highres_timer(void);
+
+/*
+ * 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_data_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;
+}
+
+#endif /* _LRNG_ES_TIMER_COMMON_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_interface_dev_common.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_INTERFACE_DEV_COMMON_H
+#define _LRNG_INTERFACE_DEV_COMMON_H
+
+#include <linux/poll.h>
+#include <linux/wait.h>
+
+/******************* Upstream functions hooked into the LRNG ******************/
+enum lrng_external_noise_source {
+ lrng_noise_source_hw,
+ lrng_noise_source_user
+};
+
+#ifdef CONFIG_LRNG_COMMON_DEV_IF
+void lrng_writer_wakeup(void);
+void lrng_init_wakeup_dev(void);
+void lrng_state_exseed_set(enum lrng_external_noise_source source, bool type);
+void lrng_state_exseed_allow_all(void);
+#else /* CONFIG_LRNG_COMMON_DEV_IF */
+static inline void lrng_writer_wakeup(void) { }
+static inline void lrng_init_wakeup_dev(void) { }
+static inline void
+lrng_state_exseed_set(enum lrng_external_noise_source source, bool type) { }
+static inline void lrng_state_exseed_allow_all(void) { }
+#endif /* CONFIG_LRNG_COMMON_DEV_IF */
+
+/****** Downstream service functions to actual interface implementations ******/
+
+bool lrng_state_exseed_allow(enum lrng_external_noise_source source);
+int lrng_fasync(int fd, struct file *filp, int on);
+long lrng_ioctl(struct file *f, unsigned int cmd, unsigned long arg);
+ssize_t lrng_drng_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos);
+ssize_t lrng_drng_write_common(const char __user *buffer, size_t count,
+ u32 entropy_bits);
+__poll_t lrng_random_poll(struct file *file, poll_table *wait);
+ssize_t lrng_read_common_block(int nonblock, int pr,
+ char __user *buf, size_t nbytes);
+ssize_t lrng_drng_read_block(struct file *file, char __user *buf, size_t nbytes,
+ loff_t *ppos);
+ssize_t lrng_read_seed(char __user *buf, size_t nbytes, unsigned int flags);
+ssize_t lrng_read_common(char __user *buf, size_t nbytes, bool pr);
+bool lrng_need_entropy(void);
+
+extern struct wait_queue_head lrng_write_wait;
+
+#endif /* _LRNG_INTERFACE_DEV_COMMON_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_interface_random_kernel.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_INTERFACE_RANDOM_H
+#define _LRNG_INTERFACE_RANDOM_H
+
+#ifdef CONFIG_LRNG_RANDOM_IF
+void invalidate_batched_entropy(void);
+void lrng_kick_random_ready(void);
+#else /* CONFIG_LRNG_RANDOM_IF */
+static inline void invalidate_batched_entropy(void) { }
+static inline void lrng_kick_random_ready(void) { }
+#endif /* CONFIG_LRNG_RANDOM_IF */
+
+#endif /* _LRNG_INTERFACE_RANDOM_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_numa.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_NUMA_H
+#define _LRNG_NUMA_H
+
+static inline struct lrng_drng **lrng_drng_instances(void) { return NULL; }
+
+#endif /* _LRNG_NUMA_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_sha.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * LRNG SHA definition usable in atomic contexts right from the start of the
+ * kernel.
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_SHA_H
+#define _LRNG_SHA_H
+
+extern const struct lrng_hash_cb lrng_sha_hash_cb;
+
+#endif /* _LRNG_SHA_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_sha1.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Backend for the LRNG providing the SHA-1 implementation that can be used
+ * without the kernel crypto API available including during early boot and in
+ * atomic contexts.
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/lrng.h>
+#include <crypto/sha1.h>
+#include <crypto/sha1_base.h>
+
+#include "lrng_sha.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_sha1_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_sha1_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_sha1_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_sha1_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_sha1_hash_name(void)
+{
+ return "SHA-1";
+}
+
+static void lrng_sha1_hash_desc_zero(struct shash_desc *shash)
+{
+ memzero_explicit(shash_desc_ctx(shash), sizeof(struct sha1_state));
+}
+
+static void *lrng_sha1_hash_alloc(void)
+{
+ pr_info("Hash %s allocated\n", lrng_sha1_hash_name());
+ return NULL;
+}
+
+static void lrng_sha1_hash_dealloc(void *hash) { }
+
+const struct lrng_hash_cb lrng_sha_hash_cb = {
+ .hash_name = lrng_sha1_hash_name,
+ .hash_alloc = lrng_sha1_hash_alloc,
+ .hash_dealloc = lrng_sha1_hash_dealloc,
+ .hash_digestsize = lrng_sha1_hash_digestsize,
+ .hash_init = lrng_sha1_hash_init,
+ .hash_update = lrng_sha1_hash_update,
+ .hash_final = lrng_sha1_hash_final,
+ .hash_desc_zero = lrng_sha1_hash_desc_zero,
+};
--- /dev/null
+++ b/drivers/char/lrng/lrng_sha256.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Backend for the LRNG providing the SHA-256 implementation that can be used
+ * without the kernel crypto API available including during early boot and in
+ * atomic contexts.
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/lrng.h>
+#include <crypto/sha2.h>
+
+#include "lrng_sha.h"
+
+static u32 lrng_sha256_hash_digestsize(void *hash)
+{
+ return SHA256_DIGEST_SIZE;
+}
+
+static int lrng_sha256_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_sha256_hash_update(struct shash_desc *shash,
+ const u8 *inbuf, u32 inbuflen)
+{
+ sha256_update(shash_desc_ctx(shash), inbuf, inbuflen);
+ return 0;
+}
+
+static int lrng_sha256_hash_final(struct shash_desc *shash, u8 *digest)
+{
+ sha256_final(shash_desc_ctx(shash), digest);
+ return 0;
+}
+
+static const char *lrng_sha256_hash_name(void)
+{
+ return "SHA-256";
+}
+
+static void lrng_sha256_hash_desc_zero(struct shash_desc *shash)
+{
+ memzero_explicit(shash_desc_ctx(shash), sizeof(struct sha256_state));
+}
+
+static void *lrng_sha256_hash_alloc(void)
+{
+ pr_info("Hash %s allocated\n", lrng_sha256_hash_name());
+ return NULL;
+}
+
+static void lrng_sha256_hash_dealloc(void *hash) { }
+
+const struct lrng_hash_cb lrng_sha_hash_cb = {
+ .hash_name = lrng_sha256_hash_name,
+ .hash_alloc = lrng_sha256_hash_alloc,
+ .hash_dealloc = lrng_sha256_hash_dealloc,
+ .hash_digestsize = lrng_sha256_hash_digestsize,
+ .hash_init = lrng_sha256_hash_init,
+ .hash_update = lrng_sha256_hash_update,
+ .hash_final = lrng_sha256_hash_final,
+ .hash_desc_zero = lrng_sha256_hash_desc_zero,
+};
--- /dev/null
+++ b/drivers/char/lrng/lrng_sysctl.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_SYSCTL_H
+#define _LRNG_SYSCTL_H
+
+#ifdef CONFIG_LRNG_SYSCTL
+void lrng_sysctl_update_max_write_thresh(u32 new_digestsize);
+#else
+static inline void lrng_sysctl_update_max_write_thresh(u32 new_digestsize) { }
+#endif
+
+#endif /* _LRNG_SYSCTL_H */
--- /dev/null
+++ b/include/linux/lrng.h
@@ -0,0 +1,251 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_H
+#define _LRNG_H
+
+#include <crypto/hash.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+
+/*
+ * struct lrng_drng_cb - cryptographic callback functions defining a DRNG
+ * @drng_name Name of DRNG
+ * @drng_alloc: Allocate DRNG -- the provided integer should be used for
+ * sanity checks.
+ * return: allocated data structure or PTR_ERR on error
+ * @drng_dealloc: Deallocate DRNG
+ * @drng_seed: Seed the DRNG with data of arbitrary length drng: is
+ * pointer to data structure allocated with drng_alloc
+ * return: >= 0 on success, < 0 on error
+ * @drng_generate: Generate random numbers from the DRNG with arbitrary
+ * length
+ */
+struct lrng_drng_cb {
+ const char *(*drng_name)(void);
+ void *(*drng_alloc)(u32 sec_strength);
+ void (*drng_dealloc)(void *drng);
+ int (*drng_seed)(void *drng, const u8 *inbuf, u32 inbuflen);
+ int (*drng_generate)(void *drng, u8 *outbuf, u32 outbuflen);
+};
+
+/*
+ * struct lrng_hash_cb - cryptographic callback functions defining a hash
+ * @hash_name Name of Hash used for reading entropy pool arbitrary
+ * length
+ * @hash_alloc: Allocate the hash for reading the entropy pool
+ * return: allocated data structure (NULL is success too)
+ * or ERR_PTR on error
+ * @hash_dealloc: Deallocate Hash
+ * @hash_digestsize: Return the digestsize for the used hash to read out
+ * entropy pool
+ * hash: is pointer to data structure allocated with
+ * hash_alloc
+ * return: size of digest of hash in bytes
+ * @hash_init: Initialize hash
+ * hash: is pointer to data structure allocated with
+ * hash_alloc
+ * return: 0 on success, < 0 on error
+ * @hash_update: Update hash operation
+ * hash: is pointer to data structure allocated with
+ * hash_alloc
+ * return: 0 on success, < 0 on error
+ * @hash_final Final hash operation
+ * hash: is pointer to data structure allocated with
+ * hash_alloc
+ * return: 0 on success, < 0 on error
+ * @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_hash_cb {
+ const char *(*hash_name)(void);
+ void *(*hash_alloc)(void);
+ void (*hash_dealloc)(void *hash);
+ u32 (*hash_digestsize)(void *hash);
+ int (*hash_init)(struct shash_desc *shash, void *hash);
+ int (*hash_update)(struct shash_desc *shash, const u8 *inbuf,
+ u32 inbuflen);
+ int (*hash_final)(struct shash_desc *shash, u8 *digest);
+ void (*hash_desc_zero)(struct shash_desc *shash);
+};
+
+/* Register cryptographic backend */
+#ifdef CONFIG_LRNG_SWITCH
+int lrng_set_drng_cb(const struct lrng_drng_cb *cb);
+int lrng_set_hash_cb(const struct lrng_hash_cb *cb);
+#else /* CONFIG_LRNG_SWITCH */
+static inline int
+lrng_set_drng_cb(const struct lrng_drng_cb *cb) { return -EOPNOTSUPP; }
+static inline int
+lrng_set_hash_cb(const struct lrng_hash_cb *cb) { return -EOPNOTSUPP; }
+#endif /* CONFIG_LRNG_SWITCH */
+
+/* Callback to feed events to the scheduler entropy source */
+#ifdef CONFIG_LRNG_SCHED
+extern void add_sched_randomness(const struct task_struct *p, int cpu);
+#else
+static inline void
+add_sched_randomness(const struct task_struct *p, int cpu) { }
+#endif
+
+/*
+ * lrng_get_random_bytes() - Provider of cryptographic strong random numbers
+ * for kernel-internal usage.
+ *
+ * This function is appropriate for in-kernel use cases operating in atomic
+ * contexts. It will always use the ChaCha20 DRNG and it may be the case that
+ * it is not fully seeded when being used.
+ *
+ * @buf: buffer to store the random bytes
+ * @nbytes: size of the buffer
+ */
+#ifdef CONFIG_LRNG_DRNG_ATOMIC
+void lrng_get_random_bytes(void *buf, int nbytes);
+#endif
+
+/*
+ * lrng_get_random_bytes_full() - Provider of cryptographic strong
+ * random numbers for kernel-internal usage from a fully initialized LRNG.
+ *
+ * This function will always return random numbers from a fully seeded and
+ * fully initialized LRNG.
+ *
+ * This function is appropriate only for non-atomic use cases as this
+ * function may sleep. 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
+ */
+#ifdef CONFIG_LRNG
+void lrng_get_random_bytes_full(void *buf, int nbytes);
+#endif
+
+/*
+ * lrng_get_random_bytes_min() - Provider of cryptographic strong
+ * random numbers for kernel-internal usage from at least a minimally seeded
+ * LRNG, which is not necessarily fully initialized yet (e.g. SP800-90C
+ * oversampling applied in FIPS mode is not applied yet).
+ *
+ * This function is appropriate only for non-atomic use cases as this
+ * function may sleep. 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
+ */
+#ifdef CONFIG_LRNG
+void lrng_get_random_bytes_min(void *buf, int nbytes);
+#endif
+
+/*
+ * lrng_get_random_bytes_pr() - Provider of cryptographic strong
+ * random numbers for kernel-internal usage from a fully initialized LRNG and
+ * requiring a reseed from the entropy sources before.
+ *
+ * This function will always return random numbers from a fully seeded and
+ * fully initialized LRNG.
+ *
+ * This function is appropriate only for non-atomic use cases as this
+ * function may sleep. 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.
+ *
+ * This call only returns no more data than entropy was pulled from the
+ * entropy sources. Thus, it is likely that this call returns less data
+ * than requested by the caller. Also, the caller MUST be prepared that this
+ * call returns 0 bytes, i.e. it did not generate data.
+ *
+ * @buf: buffer to store the random bytes
+ * @nbytes: size of the buffer
+ *
+ * @return: positive number indicates amount of generated bytes, < 0 on error
+ */
+#ifdef CONFIG_LRNG
+int lrng_get_random_bytes_pr(void *buf, int nbytes);
+#endif
+
+/*
+ * lrng_get_seed() - Fill buffer with data from entropy sources
+ *
+ * This call allows accessing the entropy sources directly and fill the buffer
+ * with data from all available entropy sources. This filled buffer is
+ * identical to the temporary seed buffer used by the LRNG to seed its DRNGs.
+ *
+ * The call is to allows users to seed their DRNG directly from the entropy
+ * sources in case the caller does not want to use the LRNG's DRNGs. This
+ * buffer can be directly used to seed the caller's DRNG from.
+ *
+ * The call blocks as long as one LRNG DRNG is not yet fully seeded. If
+ * LRNG_GET_SEED_NONBLOCK is specified, it does not block in this case, but
+ * returns with an error.
+ *
+ * Considering SP800-90C, there is a differentiation between the seeding
+ * requirements during instantiating a DRNG and at runtime of the DRNG. When
+ * specifying LRNG_GET_SEED_FULLY_SEEDED the caller indicates the DRNG was
+ * already fully seeded and the regular amount of entropy is requested.
+ * Otherwise, the LRNG will obtain the entropy rate required for initial
+ * seeding. The following minimum entropy rates will be obtained:
+ *
+ * * FIPS mode:
+ * * Initial seeding: 384 bits of entropy
+ * * Runtime seeding: 256 bits of entropy
+ * * Non-FIPS mode:
+ * * 128 bits of entropy in any case
+ *
+ * Albeit these are minimum entropy rates, the LRNG tries to request the
+ * given amount of entropy from each entropy source individually. If the
+ * minimum amount of entropy cannot be obtained collectively by all entropy
+ * sources, the LRNG will not fill the buffer.
+ *
+ * The return data in buf is structurally equivalent to the following
+ * definition:
+ *
+ * struct {
+ * u64 seedlen;
+ * u64 entropy_rate;
+ * struct entropy_buf seed;
+ * } __attribute((__packed__));
+ *
+ * As struct entropy_buf is not known outsize of the LRNG, the LRNG fills
+ * seedlen first with the size of struct entropy_buf. If the caller-provided
+ * buffer buf is smaller than u64, then -EINVAL is returned
+ * and buf is not touched. If it is u64 or larger but smaller
+ * than the size of the structure above, -EMSGSIZE is returned and seedlen
+ * is filled with the size of the buffer. Finally, if buf is large
+ * enough to hold all data, it is filled with the seed data and the seedlen
+ * is set to sizeof(struct entropy_buf). The entropy rate is returned with
+ * the variable entropy_rate and provides the value in bits.
+ *
+ * The seed buffer is the data that should be handed to the caller's DRNG as
+ * seed data.
+ *
+ * @buf [out] Buffer to be filled with data from the entropy sources - note, the
+ * buffer is marked as u64 to ensure it is aligned to 64 bits.
+ * @nbytes [in] Size of the buffer allocated by the caller - this value
+ * provides size of @param buf in bytes.
+ * @flags [in] Flags field to adjust the behavior
+ *
+ * @return -EINVAL or -EMSGSIZE indicating the buffer is too small, -EAGAIN when
+ * the call would block, but NONBLOCK is specified, > 0 the size of
+ * the filled buffer.
+ */
+#ifdef CONFIG_LRNG
+enum lrng_get_seed_flags {
+ LRNG_GET_SEED_NONBLOCK = 0x0001, /**< Do not block the call */
+ LRNG_GET_SEED_FULLY_SEEDED = 0x0002, /**< DRNG is fully seeded */
+};
+
+ssize_t lrng_get_seed(u64 *buf, size_t nbytes, unsigned int flags);
+#endif
+
+#endif /* _LRNG_H */
From c214eec976715ae46ed8e6eee5a2147c8fd155eb Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Sun, 15 May 2022 15:40:46 +0200
Subject: [PATCH 02/25] 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.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Makefile | 2 +
drivers/char/lrng/lrng_numa.c | 124 ++++++++++++++++++++++++++++++++++
drivers/char/lrng/lrng_numa.h | 4 ++
drivers/char/lrng/lrng_proc.h | 11 +++
4 files changed, 141 insertions(+)
create mode 100644 drivers/char/lrng/lrng_numa.c
create mode 100644 drivers/char/lrng/lrng_proc.h
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -8,4 +8,6 @@ obj-y += lrng_es_mgr.o lrng_drng_mgr
obj-$(CONFIG_LRNG_SHA256) += lrng_sha256.o
obj-$(CONFIG_LRNG_SHA1) += lrng_sha1.o
+obj-$(CONFIG_NUMA) += lrng_numa.o
+
obj-$(CONFIG_LRNG_DRNG_CHACHA20) += lrng_drng_chacha20.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_numa.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG NUMA support
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/lrng.h>
+#include <linux/slab.h>
+
+#include "lrng_drng_mgr.h"
+#include "lrng_es_irq.h"
+#include "lrng_es_mgr.h"
+#include "lrng_numa.h"
+#include "lrng_proc.h"
+
+static struct lrng_drng **lrng_drng __read_mostly = NULL;
+
+struct lrng_drng **lrng_drng_instances(void)
+{
+ /* counterpart to cmpxchg_release in _lrng_drngs_numa_alloc */
+ return READ_ONCE(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;
+
+ /* Make sure the initial DRNG is initialized and its drng_cb is set */
+ if (lrng_drng_initalize())
+ goto err;
+
+ 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));
+
+ if (lrng_drng_alloc_common(drng, lrng_drng_init->drng_cb)) {
+ kfree(drng);
+ goto err;
+ }
+
+ drng->hash_cb = lrng_drng_init->hash_cb;
+ drng->hash = lrng_drng_init->hash_cb->hash_alloc();
+ if (IS_ERR(drng->hash)) {
+ lrng_drng_init->drng_cb->drng_dealloc(drng->drng);
+ kfree(drng);
+ goto err;
+ }
+
+ mutex_init(&drng->lock);
+ rwlock_init(&drng->hash_lock);
+
+ /*
+ * No reseeding of NUMA DRNGs from previous DRNGs as this
+ * would complicate the code. Let it simply reseed.
+ */
+ 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 READ_ONCE 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) {
+ drng->hash_cb->hash_dealloc(drng->hash);
+ drng->drng_cb->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);
+
+static void lrng_drngs_numa_alloc(void)
+{
+ schedule_work(&lrng_drngs_numa_alloc_work);
+}
+
+static int __init lrng_numa_init(void)
+{
+ lrng_drngs_numa_alloc();
+ return 0;
+}
+
+late_initcall(lrng_numa_init);
--- a/drivers/char/lrng/lrng_numa.h
+++ b/drivers/char/lrng/lrng_numa.h
@@ -6,6 +6,10 @@
#ifndef _LRNG_NUMA_H
#define _LRNG_NUMA_H
+#ifdef CONFIG_NUMA
+struct lrng_drng **lrng_drng_instances(void);
+#else /* CONFIG_NUMA */
static inline struct lrng_drng **lrng_drng_instances(void) { return NULL; }
+#endif /* CONFIG_NUMA */
#endif /* _LRNG_NUMA_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_proc.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_PROC_H
+#define _LRNG_PROC_H
+
+static inline void lrng_pool_inc_numa_node(void) { }
+
+#endif /* _LRNG_PROC_H */
From b97aca849edc371e2586ab0a2f12908910cfa500 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Sun, 18 Dec 2022 21:12:42 +0100
Subject: [PATCH 03/25] LRNG - /proc interface
The patch adds 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)
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_proc.c | 74 +++++++++++++++++++++++++++++++++++
drivers/char/lrng/lrng_proc.h | 4 ++
3 files changed, 79 insertions(+)
create mode 100644 drivers/char/lrng/lrng_proc.c
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -8,6 +8,7 @@ obj-y += lrng_es_mgr.o lrng_drng_mgr
obj-$(CONFIG_LRNG_SHA256) += lrng_sha256.o
obj-$(CONFIG_LRNG_SHA1) += lrng_sha1.o
+obj-$(CONFIG_SYSCTL) += lrng_proc.o
obj-$(CONFIG_NUMA) += lrng_numa.o
obj-$(CONFIG_LRNG_DRNG_CHACHA20) += lrng_drng_chacha20.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_proc.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG proc interfaces
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#include <linux/lrng.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include "lrng_drng_mgr.h"
+#include "lrng_es_aux.h"
+#include "lrng_es_mgr.h"
+#include "lrng_proc.h"
+
+/* 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 char buf[270];
+ u32 i;
+
+ mutex_lock(&lrng_drng_init->lock);
+ snprintf(buf, sizeof(buf),
+ "DRNG name: %s\n"
+ "LRNG security strength in bits: %d\n"
+ "Number of DRNG instances: %u\n"
+ "Standards compliance: %sNTG.1 (2011%s)\n"
+ "LRNG minimally seeded: %s\n"
+ "LRNG fully seeded: %s\n"
+ "LRNG entropy level: %u\n",
+ lrng_drng_init->drng_cb->drng_name(),
+ lrng_security_strength(),
+ numa_drngs,
+ lrng_sp80090c_compliant() ? "SP800-90C, " : "",
+ lrng_ntg1_2022_compliant() ? " / 2022" : "",
+ lrng_state_min_seeded() ? "true" : "false",
+ lrng_state_fully_seeded() ? "true" : "false",
+ lrng_avail_entropy());
+ seq_write(m, buf, strlen(buf));
+
+ for_each_lrng_es(i) {
+ snprintf(buf, sizeof(buf),
+ "Entropy Source %u properties:\n"
+ " Name: %s\n",
+ i, lrng_es[i]->name);
+ seq_write(m, buf, strlen(buf));
+
+ buf[0] = '\0';
+ lrng_es[i]->state(buf, sizeof(buf));
+ seq_write(m, buf, strlen(buf));
+ }
+
+ mutex_unlock(&lrng_drng_init->lock);
+
+ 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);
--- a/drivers/char/lrng/lrng_proc.h
+++ b/drivers/char/lrng/lrng_proc.h
@@ -6,6 +6,10 @@
#ifndef _LRNG_PROC_H
#define _LRNG_PROC_H
+#ifdef CONFIG_SYSCTL
+void lrng_pool_inc_numa_node(void);
+#else
static inline void lrng_pool_inc_numa_node(void) { }
+#endif
#endif /* _LRNG_PROC_H */
From 074dffdaefb5398b95a733f38aa58c973f918e20 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Mon, 20 Feb 2023 22:02:06 +0100
Subject: [PATCH 04/25] 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.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 8 +-
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_switch.c | 286 ++++++++++++++++++++++++++++++++
3 files changed, 291 insertions(+), 4 deletions(-)
create mode 100644 drivers/char/lrng/lrng_switch.c
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -629,10 +629,10 @@ config LRNG_DRNG_CHACHA20
# tristate
# depends on CRYPTO
# select CRYPTO_RNG
-#
-# config LRNG_SWITCH
-# bool
-#
+
+config LRNG_SWITCH
+ bool
+
# menuconfig LRNG_SWITCH_HASH
# bool "Support conditioning hash runtime switching"
# select LRNG_SWITCH
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -11,4 +11,5 @@ obj-$(CONFIG_LRNG_SHA1) += lrng_sha1.o
obj-$(CONFIG_SYSCTL) += lrng_proc.o
obj-$(CONFIG_NUMA) += lrng_numa.o
+obj-$(CONFIG_LRNG_SWITCH) += lrng_switch.o
obj-$(CONFIG_LRNG_DRNG_CHACHA20) += lrng_drng_chacha20.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_switch.c
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG DRNG switching support
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/lrng.h>
+
+#include "lrng_es_aux.h"
+#include "lrng_es_mgr.h"
+#include "lrng_interface_dev_common.h"
+#include "lrng_numa.h"
+
+static int __maybe_unused
+lrng_hash_switch(struct lrng_drng *drng_store, const void *cb, int node)
+{
+ const struct lrng_hash_cb *new_cb = (const struct lrng_hash_cb *)cb;
+ const struct lrng_hash_cb *old_cb = drng_store->hash_cb;
+ unsigned long flags;
+ u32 i;
+ void *new_hash, *old_hash;
+ int ret;
+
+ if (node == -1)
+ return 0;
+
+ new_hash = new_cb->hash_alloc();
+ old_hash = drng_store->hash;
+
+ if (IS_ERR(new_hash)) {
+ pr_warn("could not allocate new LRNG pool hash (%ld)\n",
+ PTR_ERR(new_hash));
+ return PTR_ERR(new_hash);
+ }
+
+ if (new_cb->hash_digestsize(new_hash) > LRNG_MAX_DIGESTSIZE) {
+ pr_warn("digest size of newly requested hash too large\n");
+ new_cb->hash_dealloc(new_hash);
+ return -EINVAL;
+ }
+
+ write_lock_irqsave(&drng_store->hash_lock, flags);
+
+ /* Trigger the switch for each entropy source */
+ for_each_lrng_es(i) {
+ if (!lrng_es[i]->switch_hash)
+ continue;
+ ret = lrng_es[i]->switch_hash(drng_store, node, new_cb,
+ new_hash, old_cb);
+ if (ret) {
+ u32 j;
+
+ /* Revert all already executed operations */
+ for (j = 0; j < i; j++) {
+ if (!lrng_es[j]->switch_hash)
+ continue;
+ WARN_ON(lrng_es[j]->switch_hash(drng_store,
+ node, old_cb,
+ old_hash,
+ new_cb));
+ }
+ goto err;
+ }
+ }
+
+ drng_store->hash = new_hash;
+ drng_store->hash_cb = new_cb;
+ old_cb->hash_dealloc(old_hash);
+ pr_info("Conditioning function allocated for DRNG for NUMA node %d\n",
+ node);
+
+err:
+ write_unlock_irqrestore(&drng_store->hash_lock, flags);
+ return ret;
+}
+
+static int __maybe_unused
+lrng_drng_switch(struct lrng_drng *drng_store, const void *cb, int node)
+{
+ const struct lrng_drng_cb *new_cb = (const struct lrng_drng_cb *)cb;
+ const struct lrng_drng_cb *old_cb = drng_store->drng_cb;
+ int ret;
+ u8 seed[LRNG_DRNG_SECURITY_STRENGTH_BYTES];
+ void *new_drng = new_cb->drng_alloc(LRNG_DRNG_SECURITY_STRENGTH_BYTES);
+ void *old_drng = drng_store->drng;
+ u32 current_security_strength;
+ bool 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);
+ }
+
+ current_security_strength = lrng_security_strength();
+ mutex_lock(&drng_store->lock);
+
+ /*
+ * 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 = old_cb->drng_generate(old_drng, seed, sizeof(seed));
+ mutex_unlock(&drng_store->lock);
+
+ 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 = new_cb->drng_seed(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);
+
+ if (reset_drng)
+ lrng_drng_reset(drng_store);
+
+ drng_store->drng = new_drng;
+ drng_store->drng_cb = new_cb;
+
+ /* 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_CRYPTO_FIPS))
+ lrng_unset_fully_seeded(drng_store);
+
+ if (lrng_state_min_seeded())
+ lrng_set_entropy_thresh(lrng_get_seed_entropy_osr(
+ drng_store->fully_seeded));
+
+ old_cb->drng_dealloc(old_drng);
+
+ pr_info("DRNG of NUMA node %d switched\n", node);
+
+ mutex_unlock(&drng_store->lock);
+ return ret;
+}
+
+/*
+ * Switch the existing DRNG and hash instances with new using the new crypto
+ * callbacks. The caller must hold the lrng_crypto_cb_update lock.
+ */
+static int lrng_switch(const void *cb,
+ int (*switcher)(struct lrng_drng *drng_store,
+ const void *cb, int node))
+{
+ struct lrng_drng **lrng_drng = lrng_drng_instances();
+ struct lrng_drng *lrng_drng_init = lrng_drng_init_instance();
+ struct lrng_drng *lrng_drng_pr = lrng_drng_pr_instance();
+ int ret = 0;
+
+ if (lrng_drng) {
+ u32 node;
+
+ for_each_online_node(node) {
+ if (lrng_drng[node])
+ ret |= switcher(lrng_drng[node], cb, node);
+ }
+ } else {
+ ret |= switcher(lrng_drng_init, cb, 0);
+ }
+
+ ret |= switcher(lrng_drng_pr, cb, -1);
+
+ return ret;
+}
+
+/*
+ * 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.
+ *
+ * drng_cb: Callback functions to be registered -- if NULL, use the default
+ * callbacks defined at compile time.
+ *
+ * Return:
+ * * 0 on success
+ * * < 0 on error
+ */
+int lrng_set_drng_cb(const struct lrng_drng_cb *drng_cb)
+{
+ struct lrng_drng *lrng_drng_init = lrng_drng_init_instance();
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_LRNG_SWITCH_DRNG))
+ return -EOPNOTSUPP;
+
+ if (!drng_cb)
+ drng_cb = lrng_default_drng_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 ((drng_cb != lrng_default_drng_cb) &&
+ (lrng_drng_init->drng_cb != lrng_default_drng_cb)) {
+ pr_warn("disallow setting new DRNG callbacks, unload the old callbacks first!\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = lrng_switch(drng_cb, lrng_drng_switch);
+ /* The switch may imply new entropy due to larger DRNG sec strength. */
+ if (!ret)
+ lrng_es_add_entropy();
+
+out:
+ mutex_unlock(&lrng_crypto_cb_update);
+ return ret;
+}
+EXPORT_SYMBOL(lrng_set_drng_cb);
+
+/*
+ * lrng_set_hash_cb - Register new cryptographic callback functions for hash
+ * The registering implies that all old hash states are replaced with new
+ * hash states.
+ *
+ * @hash_cb: Callback functions to be registered -- if NULL, use the default
+ * callbacks defined at compile time.
+ *
+ * Return:
+ * * 0 on success
+ * * < 0 on error
+ */
+int lrng_set_hash_cb(const struct lrng_hash_cb *hash_cb)
+{
+ struct lrng_drng *lrng_drng_init = lrng_drng_init_instance();
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_LRNG_SWITCH_HASH))
+ return -EOPNOTSUPP;
+
+ if (!hash_cb)
+ hash_cb = lrng_default_hash_cb;
+
+ mutex_lock(&lrng_crypto_cb_update);
+
+ /* Comment from lrng_set_drng_cb applies. */
+ if ((hash_cb != lrng_default_hash_cb) &&
+ (lrng_drng_init->hash_cb != lrng_default_hash_cb)) {
+ pr_warn("disallow setting new hash callbacks, unload the old callbacks first!\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = lrng_switch(hash_cb, lrng_hash_switch);
+ /*
+ * The switch may imply new entropy due to larger digest size. But
+ * it may also offer more room in the aux pool which means we ping
+ * any waiting entropy providers.
+ */
+ if (!ret) {
+ lrng_es_add_entropy();
+ lrng_writer_wakeup();
+ }
+
+out:
+ mutex_unlock(&lrng_crypto_cb_update);
+ return ret;
+}
+EXPORT_SYMBOL(lrng_set_hash_cb);
From 6db232ea9ebcca23650d15e3606a8f4d48c09ca1 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Sun, 15 May 2022 16:01:44 +0200
Subject: [PATCH 05/25] 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.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 40 ++++----
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_hash_kcapi.c | 140 ++++++++++++++++++++++++++++
3 files changed, 161 insertions(+), 20 deletions(-)
create mode 100644 drivers/char/lrng/lrng_hash_kcapi.c
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -633,26 +633,26 @@ config LRNG_DRNG_CHACHA20
config LRNG_SWITCH
bool
-# menuconfig LRNG_SWITCH_HASH
-# bool "Support conditioning hash runtime switching"
-# select LRNG_SWITCH
-# help
-# The LRNG uses a default message digest. With this
-# configuration option other message digests can be selected
-# and loaded at runtime.
-#
-# if LRNG_SWITCH_HASH
-#
-# config LRNG_HASH_KCAPI
-# tristate "Kernel crypto API hashing support for LRNG"
-# select CRYPTO_HASH
-# select CRYPTO_SHA512
-# help
-# Enable the kernel crypto API support for entropy compression
-# and conditioning functions.
-#
-# endif # LRNG_SWITCH_HASH
-#
+menuconfig LRNG_SWITCH_HASH
+ bool "Support conditioning hash runtime switching"
+ select LRNG_SWITCH
+ help
+ The LRNG uses a default message digest. With this
+ configuration option other message digests can be selected
+ and loaded at runtime.
+
+if LRNG_SWITCH_HASH
+
+config LRNG_HASH_KCAPI
+ tristate "Kernel crypto API hashing support for LRNG"
+ select CRYPTO_HASH
+ select CRYPTO_SHA512
+ help
+ Enable the kernel crypto API support for entropy compression
+ and conditioning functions.
+
+endif # LRNG_SWITCH_HASH
+
# menuconfig LRNG_SWITCH_DRNG
# bool "Support DRNG runtime switching"
# select LRNG_SWITCH
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -12,4 +12,5 @@ obj-$(CONFIG_SYSCTL) += lrng_proc.o
obj-$(CONFIG_NUMA) += lrng_numa.o
obj-$(CONFIG_LRNG_SWITCH) += lrng_switch.o
+obj-$(CONFIG_LRNG_HASH_KCAPI) += lrng_hash_kcapi.o
obj-$(CONFIG_LRNG_DRNG_CHACHA20) += lrng_drng_chacha20.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_hash_kcapi.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Backend for providing the hash primitive using the kernel crypto API.
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/lrng.h>
+#include <crypto/hash.h>
+#include <linux/module.h>
+
+
+static char *lrng_hash_name = "sha512";
+
+/* The parameter must be r/o in sysfs as otherwise races appear. */
+module_param(lrng_hash_name, charp, 0444);
+MODULE_PARM_DESC(lrng_hash_name, "Kernel crypto API hash name");
+
+struct lrng_hash_info {
+ struct crypto_shash *tfm;
+};
+
+static const char *lrng_kcapi_hash_name(void)
+{
+ return lrng_hash_name;
+}
+
+static 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);
+}
+
+static 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;
+}
+
+static void *lrng_kcapi_hash_name_alloc(void)
+{
+ return lrng_kcapi_hash_alloc(lrng_kcapi_hash_name());
+}
+
+static 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);
+}
+
+static 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");
+}
+
+static 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);
+}
+
+static int lrng_kcapi_hash_update(struct shash_desc *shash, const u8 *inbuf,
+ u32 inbuflen)
+{
+ return crypto_shash_update(shash, inbuf, inbuflen);
+}
+
+static int lrng_kcapi_hash_final(struct shash_desc *shash, u8 *digest)
+{
+ return crypto_shash_final(shash, digest);
+}
+
+static void lrng_kcapi_hash_zero(struct shash_desc *shash)
+{
+ shash_desc_zero(shash);
+}
+
+static const struct lrng_hash_cb lrng_kcapi_hash_cb = {
+ .hash_name = lrng_kcapi_hash_name,
+ .hash_alloc = lrng_kcapi_hash_name_alloc,
+ .hash_dealloc = lrng_kcapi_hash_dealloc,
+ .hash_digestsize = lrng_kcapi_hash_digestsize,
+ .hash_init = lrng_kcapi_hash_init,
+ .hash_update = lrng_kcapi_hash_update,
+ .hash_final = lrng_kcapi_hash_final,
+ .hash_desc_zero = lrng_kcapi_hash_zero,
+};
+
+static int __init lrng_kcapi_init(void)
+{
+ return lrng_set_hash_cb(&lrng_kcapi_hash_cb);
+}
+
+static void __exit lrng_kcapi_exit(void)
+{
+ lrng_set_hash_cb(NULL);
+}
+
+late_initcall(lrng_kcapi_init);
+module_exit(lrng_kcapi_exit);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
+MODULE_DESCRIPTION("Entropy Source and DRNG Manager - Kernel crypto API hash backend");
From b59bd03bfd2776058531dc6f9573a0e0a46a23d2 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Thu, 21 Mar 2024 14:17:33 +0100
Subject: [PATCH 06/25] 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.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
crypto/drbg.c | 16 ++++++++++------
include/crypto/drbg.h | 7 +++++++
2 files changed, 17 insertions(+), 6 deletions(-)
--- a/crypto/drbg.c
+++ b/crypto/drbg.c
@@ -115,7 +115,7 @@
* HMAC-SHA512 / 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,
@@ -180,6 +180,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);
@@ -195,7 +196,7 @@ static int drbg_uninstantiate(struct drb
* 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:
@@ -208,6 +209,7 @@ static inline unsigned short drbg_sec_st
return 32;
}
}
+EXPORT_SYMBOL(drbg_sec_strength);
/*
* FIPS 140-2 continuous self test for the noise source
@@ -1236,7 +1238,7 @@ out:
}
/* 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;
@@ -1257,12 +1259,13 @@ static inline void drbg_dealloc_state(st
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;
@@ -1343,6 +1346,7 @@ err:
drbg_dealloc_state(drbg);
return ret;
}
+EXPORT_SYMBOL(drbg_alloc_state);
/*************************************************************************
* DRBG interface functions
@@ -1877,8 +1881,7 @@ out:
*
* 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;
@@ -1905,6 +1908,7 @@ static inline void drbg_convert_tfm_core
}
}
}
+EXPORT_SYMBOL(drbg_convert_tfm_core);
static int drbg_kcapi_init(struct crypto_tfm *tfm)
{
--- a/include/crypto/drbg.h
+++ b/include/crypto/drbg.h
@@ -283,4 +283,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 */
From ed6c12a2aeac7927392151adcbf947c3c0865988 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Sun, 28 Jul 2024 21:39:01 +0200
Subject: [PATCH 07/25] 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.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 78 +++++------
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_drng_drbg.c | 215 +++++++++++++++++++++++++++++
3 files changed, 255 insertions(+), 39 deletions(-)
create mode 100644 drivers/char/lrng/lrng_drng_drbg.c
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -620,11 +620,11 @@ endmenu # "Specific DRNG seeding strateg
config LRNG_DRNG_CHACHA20
tristate
-# config LRNG_DRBG
-# tristate
-# depends on CRYPTO
-# select CRYPTO_DRBG_MENU
-#
+config LRNG_DRBG
+ tristate
+ depends on CRYPTO
+ select CRYPTO_DRBG_MENU
+
# config LRNG_DRNG_KCAPI
# tristate
# depends on CRYPTO
@@ -653,33 +653,33 @@ config LRNG_HASH_KCAPI
endif # LRNG_SWITCH_HASH
-# menuconfig LRNG_SWITCH_DRNG
-# bool "Support DRNG runtime switching"
-# select LRNG_SWITCH
-# help
-# The LRNG uses a default DRNG With this configuration
-# option other DRNGs or message digests can be selected and
-# loaded at runtime.
-#
-# if LRNG_SWITCH_DRNG
-#
-# config LRNG_SWITCH_DRNG_CHACHA20
-# tristate "ChaCha20-based DRNG support for LRNG"
-# depends on !LRNG_DFLT_DRNG_CHACHA20
-# select LRNG_DRNG_CHACHA20
-# help
-# Enable the ChaCha20-based DRNG. This DRNG implementation
-# does not depend on the kernel crypto API presence.
-#
-# config LRNG_SWITCH_DRBG
-# tristate "SP800-90A support for the LRNG"
-# depends on !LRNG_DFLT_DRNG_DRBG
-# select LRNG_DRBG
-# 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.
-#
+menuconfig LRNG_SWITCH_DRNG
+ bool "Support DRNG runtime switching"
+ select LRNG_SWITCH
+ help
+ The LRNG uses a default DRNG With this configuration
+ option other DRNGs or message digests can be selected and
+ loaded at runtime.
+
+if LRNG_SWITCH_DRNG
+
+config LRNG_SWITCH_DRNG_CHACHA20
+ tristate "ChaCha20-based DRNG support for LRNG"
+ depends on !LRNG_DFLT_DRNG_CHACHA20
+ select LRNG_DRNG_CHACHA20
+ help
+ Enable the ChaCha20-based DRNG. This DRNG implementation
+ does not depend on the kernel crypto API presence.
+
+config LRNG_SWITCH_DRBG
+ tristate "SP800-90A support for the LRNG"
+ depends on !LRNG_DFLT_DRNG_DRBG
+ select LRNG_DRBG
+ 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.
+
# config LRNG_SWITCH_DRNG_KCAPI
# tristate "Kernel Crypto API support for the LRNG"
# depends on !LRNG_DFLT_DRNG_KCAPI
@@ -691,8 +691,8 @@ endif # LRNG_SWITCH_HASH
# 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_SWITCH_DRNG
+
+endif # LRNG_SWITCH_DRNG
choice
prompt "LRNG Default DRNG"
@@ -707,11 +707,11 @@ choice
bool "ChaCha20-based DRNG"
select LRNG_DRNG_CHACHA20
-# config LRNG_DFLT_DRNG_DRBG
-# depends on RANDOM_DEFAULT_IMPL
-# bool "SP800-90A DRBG"
-# select LRNG_DRBG
-#
+ config LRNG_DFLT_DRNG_DRBG
+ depends on RANDOM_DEFAULT_IMPL
+ bool "SP800-90A DRBG"
+ select LRNG_DRBG
+
# config LRNG_DFLT_DRNG_KCAPI
# depends on RANDOM_DEFAULT_IMPL
# bool "Kernel Crypto API DRNG"
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_NUMA) += lrng_numa.o
obj-$(CONFIG_LRNG_SWITCH) += lrng_switch.o
obj-$(CONFIG_LRNG_HASH_KCAPI) += lrng_hash_kcapi.o
obj-$(CONFIG_LRNG_DRNG_CHACHA20) += lrng_drng_chacha20.o
+obj-$(CONFIG_LRNG_DRBG) += lrng_drng_drbg.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_drng_drbg.c
@@ -0,0 +1,215 @@
+// 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) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <crypto/drbg.h>
+#include <linux/init.h>
+#include <linux/lrng.h>
+#include <linux/module.h>
+
+#include "lrng_drng_drbg.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 */
+ .drbg_core = "drbg_nopr_ctr_aes256",
+ }, {
+ /* HMAC_DRBG with SHA-512 */
+ .drbg_core = "drbg_nopr_hmac_sha512",
+ }, {
+ /* Hash_DRBG with SHA-512 using derivation function */
+ .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 = DRBG_SEED_STATE_FULL;
+
+ 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 = DRBG_SEED_STATE_UNSEEDED;
+ 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 const char* lrng_drbg_name(void)
+{
+ return lrng_drbg_types[lrng_drbg_type].drbg_core;
+}
+
+const struct lrng_drng_cb lrng_drbg_cb = {
+ .drng_name = lrng_drbg_name,
+ .drng_alloc = lrng_drbg_drng_alloc,
+ .drng_dealloc = lrng_drbg_drng_dealloc,
+ .drng_seed = lrng_drbg_drng_seed_helper,
+ .drng_generate = lrng_drbg_drng_generate_helper,
+};
+
+static int __init lrng_drbg_selftest(void)
+{
+ struct crypto_rng *drbg;
+
+ /* Allocate the DRBG once to trigger the kernel crypto API self test */
+ drbg = crypto_alloc_rng(lrng_drbg_types[lrng_drbg_type].drbg_core, 0,
+ 0);
+ if (IS_ERR(drbg)) {
+ pr_err("could not allocate DRBG and trigger self-test: %ld\n",
+ PTR_ERR(drbg));
+ return PTR_ERR(drbg);
+ }
+ crypto_free_rng(drbg);
+
+ return 0;
+}
+
+#ifndef CONFIG_LRNG_DFLT_DRNG_DRBG
+static int __init lrng_drbg_init(void)
+{
+ int ret = lrng_drbg_selftest();
+
+ if (ret)
+ return ret;
+
+ 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_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 <smueller@chronox.de>");
+MODULE_DESCRIPTION("Entropy Source and DRNG Manager - SP800-90A DRBG backend");
+#else
+
+/*
+ * Note, this call may result in the use of the DRBG before the self-test is
+ * run. However, that usage is not relevant to any FIPS-140 consideration as
+ * the data is used for non-cryptographic purposes. The call below guarantees
+ * that the self-tests are run before user space is started and thus callers
+ * with needs to comply with FIPS-140 appear.
+ */
+late_initcall(lrng_drbg_selftest);
+
+#endif /* CONFIG_LRNG_DFLT_DRNG_DRBG */
From abd4e08622156956150d80bf4e8cd4b4c31113ef Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Mon, 20 Feb 2023 22:23:59 +0100
Subject: [PATCH 08/25] 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.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 38 ++---
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_drng_kcapi.c | 208 ++++++++++++++++++++++++++++
3 files changed, 228 insertions(+), 19 deletions(-)
create mode 100644 drivers/char/lrng/lrng_drng_kcapi.c
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -625,10 +625,10 @@ config LRNG_DRBG
depends on CRYPTO
select CRYPTO_DRBG_MENU
-# config LRNG_DRNG_KCAPI
-# tristate
-# depends on CRYPTO
-# select CRYPTO_RNG
+config LRNG_DRNG_KCAPI
+ tristate
+ depends on CRYPTO
+ select CRYPTO_RNG
config LRNG_SWITCH
bool
@@ -680,17 +680,17 @@ config LRNG_SWITCH_DRBG
module is loaded, output from /dev/random, /dev/urandom,
getrandom(2), or get_random_bytes_full is provided by a DRBG.
-# config LRNG_SWITCH_DRNG_KCAPI
-# tristate "Kernel Crypto API support for the LRNG"
-# depends on !LRNG_DFLT_DRNG_KCAPI
-# depends on !LRNG_SWITCH_DRBG
-# select LRNG_DRNG_KCAPI
-# 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.
+config LRNG_SWITCH_DRNG_KCAPI
+ tristate "Kernel Crypto API support for the LRNG"
+ depends on !LRNG_DFLT_DRNG_KCAPI
+ depends on !LRNG_SWITCH_DRBG
+ select LRNG_DRNG_KCAPI
+ 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_SWITCH_DRNG
@@ -712,10 +712,10 @@ choice
bool "SP800-90A DRBG"
select LRNG_DRBG
-# config LRNG_DFLT_DRNG_KCAPI
-# depends on RANDOM_DEFAULT_IMPL
-# bool "Kernel Crypto API DRNG"
-# select LRNG_DRNG_KCAPI
+ config LRNG_DFLT_DRNG_KCAPI
+ depends on RANDOM_DEFAULT_IMPL
+ bool "Kernel Crypto API DRNG"
+ select LRNG_DRNG_KCAPI
endchoice
# menuconfig LRNG_TESTING_MENU
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -15,3 +15,4 @@ obj-$(CONFIG_LRNG_SWITCH) += lrng_switc
obj-$(CONFIG_LRNG_HASH_KCAPI) += lrng_hash_kcapi.o
obj-$(CONFIG_LRNG_DRNG_CHACHA20) += lrng_drng_chacha20.o
obj-$(CONFIG_LRNG_DRBG) += lrng_drng_drbg.o
+obj-$(CONFIG_LRNG_DRNG_KCAPI) += lrng_drng_kcapi.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_drng_kcapi.c
@@ -0,0 +1,208 @@
+// 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) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/lrng.h>
+#include <crypto/hash.h>
+#include <crypto/rng.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "lrng_drng_kcapi.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 *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;
+ struct crypto_shash *hash_tfm;
+};
+
+static int lrng_kcapi_drng_seed_helper(void *drng, const u8 *inbuf,
+ u32 inbuflen)
+{
+ struct lrng_drng_info *lrng_drng_info = (struct lrng_drng_info *)drng;
+ struct crypto_rng *kcapi_rng = lrng_drng_info->kcapi_rng;
+ struct crypto_shash *hash_tfm = lrng_drng_info->hash_tfm;
+ SHASH_DESC_ON_STACK(shash, hash_tfm);
+ u32 digestsize;
+ u8 digest[HASH_MAX_DIGESTSIZE] __aligned(8);
+ int ret;
+
+ if (!hash_tfm)
+ return crypto_rng_reset(kcapi_rng, inbuf, inbuflen);
+
+ shash->tfm = hash_tfm;
+ digestsize = crypto_shash_digestsize(hash_tfm);
+
+ ret = crypto_shash_digest(shash, inbuf, inbuflen, digest);
+ shash_desc_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;
+ u32 time = random_get_entropy();
+ int seedsize, rv;
+ void *ret = ERR_PTR(-ENOMEM);
+
+ if (!drng_name) {
+ pr_err("DRNG name missing\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!memcmp(drng_name, "stdrng", 6) ||
+ !memcmp(drng_name, "lrng", 4) ||
+ !memcmp(drng_name, "drbg", 4) ||
+ !memcmp(drng_name, "jitterentropy_rng", 17)) {
+ pr_err("Refusing to load the requested random number generator\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ lrng_drng_info = kzalloc(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 (seedsize) {
+ struct crypto_shash *hash_tfm;
+
+ 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;
+ }
+ }
+
+ hash_tfm = crypto_alloc_shash(seed_hash, 0, 0);
+ if (IS_ERR(hash_tfm)) {
+ ret = ERR_CAST(hash_tfm);
+ goto dealloc;
+ }
+
+ if (seedsize != crypto_shash_digestsize(hash_tfm)) {
+ pr_err("Seed hash output size not equal to DRNG seed size\n");
+ crypto_free_shash(hash_tfm);
+ ret = ERR_PTR(-EINVAL);
+ goto dealloc;
+ }
+
+ lrng_drng_info->hash_tfm = hash_tfm;
+
+ pr_info("Seed hash %s allocated\n", seed_hash);
+ }
+
+ rv = lrng_kcapi_drng_seed_helper(lrng_drng_info, (u8 *)(&time),
+ sizeof(time));
+ if (rv) {
+ ret = ERR_PTR(rv);
+ goto dealloc;
+ }
+
+ 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->hash_tfm)
+ crypto_free_shash(lrng_drng_info->hash_tfm);
+ kfree(lrng_drng_info);
+ pr_info("DRNG %s deallocated\n", drng_name);
+}
+
+static const char *lrng_kcapi_drng_name(void)
+{
+ return drng_name;
+}
+
+const struct lrng_drng_cb lrng_kcapi_drng_cb = {
+ .drng_name = lrng_kcapi_drng_name,
+ .drng_alloc = lrng_kcapi_drng_alloc,
+ .drng_dealloc = lrng_kcapi_drng_dealloc,
+ .drng_seed = lrng_kcapi_drng_seed_helper,
+ .drng_generate = lrng_kcapi_drng_generate_helper,
+};
+
+#ifndef CONFIG_LRNG_DFLT_DRNG_KCAPI
+static int __init lrng_kcapi_init(void)
+{
+ return lrng_set_drng_cb(&lrng_kcapi_drng_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 <smueller@chronox.de>");
+MODULE_DESCRIPTION("Entropy Source and DRNG Manager - kernel crypto API DRNG backend");
+#endif /* CONFIG_LRNG_DFLT_DRNG_KCAPI */
From 44fd5618cd312625394fc2cd0861b2ab44068148 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Mon, 20 Feb 2023 22:05:24 +0100
Subject: [PATCH 09/25] LRNG - add atomic DRNG implementation
The atomic DRNG implementation supports the in-kernel use cases which
request random numbers in atomic contexts. It uses the ChaCha20 DRNG
which is a code base that does not sleep and it provides an interface to
callers that does not sleep. This code will only be compiled when the
LRNG enables the in-kernel drop-in replacement APIs for the existing
random.c code base.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_drng_atomic.c | 130 +++++++++++++++++++++++++++
2 files changed, 131 insertions(+)
create mode 100644 drivers/char/lrng/lrng_drng_atomic.c
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_LRNG_HASH_KCAPI) += lrng_h
obj-$(CONFIG_LRNG_DRNG_CHACHA20) += lrng_drng_chacha20.o
obj-$(CONFIG_LRNG_DRBG) += lrng_drng_drbg.o
obj-$(CONFIG_LRNG_DRNG_KCAPI) += lrng_drng_kcapi.o
+obj-$(CONFIG_LRNG_DRNG_ATOMIC) += lrng_drng_atomic.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_drng_atomic.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG DRNG for atomic contexts
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/lrng.h>
+
+#include "lrng_drng_atomic.h"
+#include "lrng_drng_chacha20.h"
+#include "lrng_es_aux.h"
+#include "lrng_es_mgr.h"
+#include "lrng_sha.h"
+
+static struct chacha20_state chacha20_atomic = {
+ LRNG_CC20_INIT_RFC7539(.block)
+};
+
+/*
+ * DRNG usable 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 = {
+ LRNG_DRNG_STATE_INIT(lrng_drng_atomic,
+ &chacha20_atomic, NULL,
+ &lrng_cc20_drng_cb, &lrng_sha_hash_cb),
+ .spin_lock = __SPIN_LOCK_UNLOCKED(lrng_drng_atomic.spin_lock)
+};
+
+struct lrng_drng *lrng_get_atomic(void)
+{
+ return &lrng_drng_atomic;
+}
+
+void lrng_drng_atomic_reset(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&lrng_drng_atomic.spin_lock, flags);
+ lrng_drng_reset(&lrng_drng_atomic);
+ spin_unlock_irqrestore(&lrng_drng_atomic.spin_lock, flags);
+}
+
+void lrng_drng_atomic_force_reseed(void)
+{
+ lrng_drng_atomic.force_reseed = lrng_drng_atomic.fully_seeded;
+}
+
+static bool lrng_drng_atomic_must_reseed(struct lrng_drng *drng)
+{
+ return (!drng->fully_seeded ||
+ atomic_read(&lrng_drng_atomic.requests) == 0 ||
+ drng->force_reseed ||
+ time_after(jiffies,
+ drng->last_seeded + lrng_drng_reseed_max_time * HZ));
+}
+
+void lrng_drng_atomic_seed_drng(struct lrng_drng *regular_drng)
+{
+ u8 seedbuf[LRNG_DRNG_SECURITY_STRENGTH_BYTES]
+ __aligned(LRNG_KCAPI_ALIGN);
+ int ret;
+
+ if (!lrng_drng_atomic_must_reseed(&lrng_drng_atomic))
+ return;
+
+ /*
+ * Reseed atomic DRNG another DRNG "regular" while this regular DRNG
+ * is reseeded. Therefore, this code operates in non-atomic context and
+ * thus can use the lrng_drng_get function to get random numbers from
+ * the just freshly seeded DRNG.
+ */
+ ret = lrng_drng_get(regular_drng, seedbuf, sizeof(seedbuf));
+
+ if (ret < 0) {
+ pr_warn("Error generating random numbers for atomic DRNG: %d\n",
+ ret);
+ } else {
+ unsigned long flags;
+
+ spin_lock_irqsave(&lrng_drng_atomic.spin_lock, flags);
+ lrng_drng_inject(&lrng_drng_atomic, seedbuf, ret,
+ regular_drng->fully_seeded, "atomic");
+ spin_unlock_irqrestore(&lrng_drng_atomic.spin_lock, flags);
+ }
+ memzero_explicit(&seedbuf, sizeof(seedbuf));
+}
+
+static void lrng_drng_atomic_get(u8 *outbuf, u32 outbuflen)
+{
+ struct lrng_drng *drng = &lrng_drng_atomic;
+ unsigned long flags;
+
+ if (!outbuf || !outbuflen)
+ return;
+
+ outbuflen = min_t(size_t, outbuflen, INT_MAX);
+
+ while (outbuflen) {
+ u32 todo = min_t(u32, outbuflen, LRNG_DRNG_MAX_REQSIZE);
+ int ret;
+
+ atomic_dec(&drng->requests);
+
+ spin_lock_irqsave(&drng->spin_lock, flags);
+ ret = drng->drng_cb->drng_generate(drng->drng, outbuf, todo);
+ spin_unlock_irqrestore(&drng->spin_lock, flags);
+ if (ret <= 0) {
+ pr_warn("getting random data from DRNG failed (%d)\n",
+ ret);
+ return;
+ }
+ outbuflen -= ret;
+ outbuf += ret;
+ }
+}
+
+void lrng_get_random_bytes(void *buf, int nbytes)
+{
+ lrng_drng_atomic_get((u8 *)buf, (u32)nbytes);
+ lrng_debug_report_seedlevel("lrng_get_random_bytes");
+}
+EXPORT_SYMBOL(lrng_get_random_bytes);
From 23fcb6113f576df31763a698e8326c8d3bab57a6 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Sun, 15 May 2022 16:21:44 +0200
Subject: [PATCH 10/25] LRNG - add common timer-based entropy source code
The code shared for timer-based entropy sources offers the following
support:
* It calculates the greatest common divisor (GCD) during startup time.
This allows diving the time stamp by this divisor to eliminate static
low-order bits. With the GCD only timer bits that move are considered
by the entropy sources.
* It contains the detector code base to identify the presence of a
high-resolution timer.
This code is only compiled when an entropy source is present that
is based on high-resolution time stamps
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 6 +-
drivers/char/lrng/Makefile | 2 +
drivers/char/lrng/lrng_es_timer_common.c | 144 +++++++++++++++++++++++
3 files changed, 149 insertions(+), 3 deletions(-)
create mode 100644 drivers/char/lrng/lrng_es_timer_common.c
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -144,9 +144,9 @@ endmenu # "Specific DRNG seeding strateg
# config LRNG_SCHED_DFLT_TIMER_ES
# bool
#
-# config LRNG_TIMER_COMMON
-# bool
-#
+config LRNG_TIMER_COMMON
+ bool
+
# choice
# prompt "Default Timer-based Entropy Source"
# default LRNG_IRQ_DFLT_TIMER_ES
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -17,3 +17,5 @@ obj-$(CONFIG_LRNG_DRNG_CHACHA20) += lrng
obj-$(CONFIG_LRNG_DRBG) += lrng_drng_drbg.o
obj-$(CONFIG_LRNG_DRNG_KCAPI) += lrng_drng_kcapi.o
obj-$(CONFIG_LRNG_DRNG_ATOMIC) += lrng_drng_atomic.o
+
+obj-$(CONFIG_LRNG_TIMER_COMMON) += lrng_es_timer_common.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_timer_common.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG Slow Entropy Source: Interrupt data collection
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/gcd.h>
+#include <linux/module.h>
+
+#include "lrng_es_irq.h"
+#include "lrng_es_sched.h"
+#include "lrng_es_timer_common.h"
+#include "lrng_health.h"
+
+/* Is high-resolution timer present? */
+static bool lrng_highres_timer_val = 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;
+
+bool lrng_gcd_tested(void)
+{
+ return (lrng_gcd_timer != 0);
+}
+
+u32 lrng_gcd_get(void)
+{
+ return lrng_gcd_timer;
+}
+
+/* Set the GCD for use in IRQ ES - if 0, the GCD calculation is restarted. */
+void lrng_gcd_set(u32 running_gcd)
+{
+ lrng_gcd_timer = running_gcd;
+ /* Ensure that update to global variable lrng_gcd_timer is visible */
+ mb();
+}
+
+static void lrng_gcd_set_check(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;
+}
+
+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_check(gcd);
+ atomic_set(&lrng_gcd_history_ptr, 0);
+ }
+}
+
+/* Return boolean whether LRNG identified presence of high-resolution timer */
+bool lrng_highres_timer(void)
+{
+ return lrng_highres_timer_val;
+}
+
+static int __init lrng_init_time_source(void)
+{
+ 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_highres_timer_val = true;
+ } else {
+ lrng_health_disable();
+ lrng_highres_timer_val = false;
+ }
+
+ lrng_irq_es_init(lrng_highres_timer_val);
+ lrng_sched_es_init(lrng_highres_timer_val);
+
+ /* Ensure that changes to global variables are visible */
+ mb();
+
+ return 0;
+}
+core_initcall(lrng_init_time_source);
From 7d4187b3588c7c0216ac82634493c30c1d5f28c7 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Tue, 25 Apr 2023 23:03:39 +0200
Subject: [PATCH 11/25] LRNG - add interrupt entropy source
The interrupt entropy source (ES) consumes the events triggered by the
kernel invoked with the add_interrupt_randomness. Its main goal is:
- to be extremely fast in the interrupt handler - This is guaranteed by
only concatenating the least significant bits of a time stamp into
CPU-local entropy pools. Thus, the operation is quasi-lockless. Also,
the concatenation is a very trivial operation. Finally, by discarding
the high-order bits, attacker-observable timing values are discarded.
- to use only cryptographic primitives for compression.
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.
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 IRQ ES is only enabled if the existing random number generator
(random.c) is not compiled.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 386 ++++++++---------
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_es_irq.c | 730 ++++++++++++++++++++++++++++++++
3 files changed, 924 insertions(+), 193 deletions(-)
create mode 100644 drivers/char/lrng/lrng_es_irq.c
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -122,8 +122,8 @@ endmenu # "Specific DRNG seeding strateg
#
# endmenu # "LRNG Interfaces"
-# menu "Entropy Source Configuration"
-#
+menu "Entropy Source Configuration"
+
# config LRNG_RUNTIME_ES_CONFIG
# bool "Enable runtime configuration of entropy sources"
# help
@@ -135,100 +135,100 @@ endmenu # "Specific DRNG seeding strateg
# a kernel command line option. When not providing any
# option, the default specified during kernel compilation
# is applied.
-#
-# comment "Common Timer-based Entropy Source Configuration"
-#
-# config LRNG_IRQ_DFLT_TIMER_ES
-# bool
-#
+
+comment "Common Timer-based Entropy Source Configuration"
+
+config LRNG_IRQ_DFLT_TIMER_ES
+ bool
+
# config LRNG_SCHED_DFLT_TIMER_ES
# bool
#
config LRNG_TIMER_COMMON
bool
-# choice
-# prompt "Default Timer-based Entropy Source"
-# default LRNG_IRQ_DFLT_TIMER_ES
-# depends on LRNG_TIMER_COMMON
-# help
-# Select the timer-based entropy source that is credited
-# with entropy. The other timer-based entropy sources may
-# be operational and provide data, but are credited with no
-# entropy.
-#
-# config LRNG_IRQ_DFLT_TIMER_ES
-# bool "Interrupt Entropy Source"
-# depends on LRNG_IRQ
-# help
-# The interrupt entropy source is selected as a timer-based
-# entropy source to provide entropy.
-#
+choice
+ prompt "Default Timer-based Entropy Source"
+ default LRNG_IRQ_DFLT_TIMER_ES
+ depends on LRNG_TIMER_COMMON
+ help
+ Select the timer-based entropy source that is credited
+ with entropy. The other timer-based entropy sources may
+ be operational and provide data, but are credited with no
+ entropy.
+
+ config LRNG_IRQ_DFLT_TIMER_ES
+ bool "Interrupt Entropy Source"
+ depends on LRNG_IRQ
+ help
+ The interrupt entropy source is selected as a timer-based
+ entropy source to provide entropy.
+
# config LRNG_SCHED_DFLT_TIMER_ES
# bool "Scheduler Entropy Source"
# depends on LRNG_SCHED
# help
# The scheduler entropy source is selected as timer-based
# entropy source to provide entropy.
-# endchoice
-#
-# choice
-# prompt "LRNG Entropy Collection Pool Size"
-# default LRNG_COLLECTION_SIZE_1024
-# depends on LRNG_TIMER_COMMON
-# help
-# Select the size of the LRNG entropy collection pool
-# storing data for the interrupt as well as the scheduler
-# entropy sources 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 !CRYPTO_FIPS
-# bool "32 interrupt events"
-#
-# config LRNG_COLLECTION_SIZE_256
-# depends on !CRYPTO_FIPS
-# 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
-#
+endchoice
+
+choice
+ prompt "LRNG Entropy Collection Pool Size"
+ default LRNG_COLLECTION_SIZE_1024
+ depends on LRNG_TIMER_COMMON
+ help
+ Select the size of the LRNG entropy collection pool
+ storing data for the interrupt as well as the scheduler
+ entropy sources 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 !CRYPTO_FIPS
+ bool "32 interrupt events"
+
+ config LRNG_COLLECTION_SIZE_256
+ depends on !CRYPTO_FIPS
+ 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_HEALTH_TESTS
# bool "Enable internal entropy source online health tests"
# depends on LRNG_TIMER_COMMON
@@ -303,113 +303,113 @@ config LRNG_TIMER_COMMON
# int
# default 371 if !LRNG_APT_BROKEN
# default 33 if LRNG_APT_BROKEN
-#
-# comment "Interrupt Entropy Source"
-#
-# config LRNG_IRQ
-# bool "Enable Interrupt Entropy Source as LRNG Seed Source"
-# default y
-# depends on !RANDOM_DEFAULT_IMPL
-# select LRNG_TIMER_COMMON
-# 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 an 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 LRNG IRQ ES 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.
-#
-# config LRNG_IRQ_ENTROPY_RATE
-# int "Interrupt Entropy Source Entropy Rate"
-# depends on LRNG_IRQ
-# range 256 4294967295 if LRNG_IRQ_DFLT_TIMER_ES
-# range 4294967295 4294967295 if !LRNG_IRQ_DFLT_TIMER_ES
-# default 256 if LRNG_IRQ_DFLT_TIMER_ES
-# default 4294967295 if !LRNG_IRQ_DFLT_TIMER_ES
-# help
-# The LRNG will collect the configured number of interrupts to
-# obtain 256 bits of entropy. This value can be set to any between
-# 256 and 4294967295. The LRNG guarantees that this value is not
-# lower than 256. This lower limit implies that one interrupt event
-# is credited with one bit of entropy. This value is subject to the
-# increase by the oversampling factor, if no high-resolution timer
-# is found.
-#
-# In order to effectively disable the interrupt entropy source,
-# the option has to be set to 4294967295. In this case, the
-# interrupt entropy source will still deliver data but without
-# being credited with entropy.
-#
+
+comment "Interrupt Entropy Source"
+
+config LRNG_IRQ
+ bool "Enable Interrupt Entropy Source as LRNG Seed Source"
+ default y
+ depends on !RANDOM_DEFAULT_IMPL
+ select LRNG_TIMER_COMMON
+ 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 an 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 LRNG IRQ ES 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.
+
+config LRNG_IRQ_ENTROPY_RATE
+ int "Interrupt Entropy Source Entropy Rate"
+ depends on LRNG_IRQ
+ range 256 4294967295 if LRNG_IRQ_DFLT_TIMER_ES
+ range 4294967295 4294967295 if !LRNG_IRQ_DFLT_TIMER_ES
+ default 256 if LRNG_IRQ_DFLT_TIMER_ES
+ default 4294967295 if !LRNG_IRQ_DFLT_TIMER_ES
+ help
+ The LRNG will collect the configured number of interrupts to
+ obtain 256 bits of entropy. This value can be set to any between
+ 256 and 4294967295. The LRNG guarantees that this value is not
+ lower than 256. This lower limit implies that one interrupt event
+ is credited with one bit of entropy. This value is subject to the
+ increase by the oversampling factor, if no high-resolution timer
+ is found.
+
+ In order to effectively disable the interrupt entropy source,
+ the option has to be set to 4294967295. In this case, the
+ interrupt entropy source will still deliver data but without
+ being credited with entropy.
+
# comment "Jitter RNG Entropy Source"
#
# config LRNG_JENT
@@ -614,8 +614,8 @@ config LRNG_TIMER_COMMON
# kernel in FIPS mode (with fips=1 kernel command line option).
# This is due to the fact that random.c is not SP800-90B
# compliant.
-#
-# endmenu # "Entropy Source Configuration"
+
+endmenu # "Entropy Source Configuration"
config LRNG_DRNG_CHACHA20
tristate
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_LRNG_DRNG_KCAPI) += lrng_d
obj-$(CONFIG_LRNG_DRNG_ATOMIC) += lrng_drng_atomic.o
obj-$(CONFIG_LRNG_TIMER_COMMON) += lrng_es_timer_common.o
+obj-$(CONFIG_LRNG_IRQ) += lrng_es_irq.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_irq.c
@@ -0,0 +1,730 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG Slow Entropy Source: Interrupt data collection
+ *
+ * Copyright (C) 2022 - 2023, Stephan Mueller <smueller@chronox.de>
+ */
+
+#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/module.h>
+#include <linux/random.h>
+
+#include "lrng_es_aux.h"
+#include "lrng_es_irq.h"
+#include "lrng_es_timer_common.h"
+#include "lrng_health.h"
+#include "lrng_numa.h"
+#include "lrng_testing.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 LRNG_UINT32_C(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;
+
+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 IRQ entropy events */
+static DEFINE_PER_CPU(u32 [LRNG_DATA_ARRAY_SIZE], lrng_irq_array)
+ __aligned(LRNG_KCAPI_ALIGN);
+static DEFINE_PER_CPU(u32, lrng_irq_array_ptr) = 0;
+static DEFINE_PER_CPU(atomic_t, lrng_irq_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_irq_continuous_compression __read_mostly =
+ IS_ENABLED(CONFIG_LRNG_ENABLE_CONTINUOUS_COMPRESSION);
+
+#ifdef CONFIG_LRNG_SWITCHABLE_CONTINUOUS_COMPRESSION
+module_param(lrng_irq_continuous_compression, bool, 0444);
+MODULE_PARM_DESC(lrng_irq_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_irq_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_irq_lock);
+static DEFINE_PER_CPU(bool, lrng_irq_lock_init) = false;
+
+static bool lrng_irq_pool_online(int cpu)
+{
+ return per_cpu(lrng_irq_lock_init, cpu);
+}
+
+static void __init lrng_irq_check_compression_state(void)
+{
+ /* One pool must hold sufficient entropy for disabled compression */
+ if (!lrng_irq_continuous_compression) {
+ u32 max_ent = min_t(u32, lrng_get_digestsize(),
+ lrng_data_to_entropy(LRNG_DATA_NUM_VALUES,
+ lrng_irq_entropy_bits));
+ if (max_ent < lrng_security_strength()) {
+ pr_warn("Force continuous compression operation to ensure LRNG can hold enough entropy\n");
+ lrng_irq_continuous_compression = true;
+ }
+ }
+}
+
+void __init lrng_irq_es_init(bool highres_timer)
+{
+ /* Set a minimum number of interrupts that must be collected */
+ irq_entropy = max_t(u32, LRNG_IRQ_ENTROPY_BITS, irq_entropy);
+
+ if (highres_timer) {
+ lrng_irq_entropy_bits = irq_entropy;
+ } else {
+ u32 new_entropy = irq_entropy * LRNG_ES_OVERSAMPLING_FACTOR;
+
+ 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_ES_OVERSAMPLING_FACTOR);
+ }
+
+ lrng_irq_check_compression_state();
+}
+
+/*
+ * Reset all per-CPU pools - reset entropy estimator but leave the pool data
+ * that may or may not have entropy unchanged.
+ */
+static void lrng_irq_reset(void)
+{
+ int cpu;
+
+ /* Trigger GCD calculation anew. */
+ lrng_gcd_set(0);
+
+ for_each_online_cpu(cpu)
+ atomic_set(per_cpu_ptr(&lrng_irq_array_irqs, cpu), 0);
+}
+
+static u32 lrng_irq_avail_pool_size(void)
+{
+ u32 max_size = 0, max_pool = lrng_get_digestsize();
+ int cpu;
+
+ if (!lrng_irq_continuous_compression)
+ max_pool = min_t(u32, max_pool, LRNG_DATA_NUM_VALUES);
+
+ for_each_online_cpu(cpu) {
+ if (lrng_irq_pool_online(cpu))
+ max_size += max_pool;
+ }
+
+ return max_size;
+}
+
+/* Return entropy of unused IRQs present in all per-CPU pools. */
+static u32 lrng_irq_avail_entropy(u32 __unused)
+{
+ u32 digestsize_irqs, irq = 0;
+ int cpu;
+
+ /* Only deliver entropy when SP800-90B self test is completed */
+ if (!lrng_sp80090b_startup_complete_es(lrng_int_es_irq))
+ return 0;
+
+ /* Obtain the cap of maximum numbers of IRQs we count */
+ digestsize_irqs = lrng_entropy_to_data(lrng_get_digestsize(),
+ lrng_irq_entropy_bits);
+ if (!lrng_irq_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_irq_pool_online(cpu))
+ continue;
+ irq += min_t(u32, digestsize_irqs,
+ atomic_read_u32(per_cpu_ptr(&lrng_irq_array_irqs,
+ cpu)));
+ }
+
+ /* Consider oversampling rate */
+ return lrng_reduce_by_osr(lrng_data_to_entropy(irq,
+ lrng_irq_entropy_bits));
+}
+
+/*
+ * 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).
+ */
+static int
+lrng_irq_switch_hash(struct lrng_drng *drng, int node,
+ const struct lrng_hash_cb *new_cb, void *new_hash,
+ const struct lrng_hash_cb *old_cb)
+{
+ u8 digest[LRNG_MAX_DIGESTSIZE];
+ u32 digestsize_irqs, found_irqs;
+ int ret = 0, cpu;
+
+ if (!IS_ENABLED(CONFIG_LRNG_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 hash_cb only applies NUMA-node-wide.
+ */
+ if (cpu_to_node(cpu) != node || !lrng_irq_pool_online(cpu))
+ continue;
+
+ pcpu_shash = (struct shash_desc *)per_cpu_ptr(lrng_irq_pool,
+ cpu);
+
+ digestsize_irqs = old_cb->hash_digestsize(pcpu_shash);
+ digestsize_irqs = lrng_entropy_to_data(digestsize_irqs << 3,
+ lrng_irq_entropy_bits);
+
+ if (pcpu_shash->tfm == new_hash)
+ continue;
+
+ /* Get the per-CPU pool hash with old digest ... */
+ ret = old_cb->hash_final(pcpu_shash, digest) ?:
+ /* ... re-initialize the hash with the new digest ... */
+ new_cb->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->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_irq_array_irqs, cpu), 0);
+ found_irqs = min_t(u32, found_irqs, digestsize_irqs);
+ atomic_add_return_relaxed(found_irqs,
+ per_cpu_ptr(&lrng_irq_array_irqs, cpu));
+
+ pr_debug("Re-initialize per-CPU interrupt entropy pool for CPU %d on NUMA node %d with hash %s\n",
+ cpu, node, new_cb->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 u32
+lrng_irq_pool_hash_one(const struct lrng_hash_cb *pcpu_hash_cb,
+ void *pcpu_hash, int cpu, u8 *digest, u32 *digestsize)
+{
+ struct shash_desc *pcpu_shash =
+ (struct shash_desc *)per_cpu_ptr(lrng_irq_pool, cpu);
+ spinlock_t *lock = per_cpu_ptr(&lrng_irq_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_hash_cb->hash_digestsize(pcpu_hash);
+ digestsize_irqs = lrng_entropy_to_data(*digestsize << 3,
+ lrng_irq_entropy_bits);
+
+ /* Obtain entropy statement like for the entropy pool */
+ found_irqs = atomic_xchg_relaxed(
+ per_cpu_ptr(&lrng_irq_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_irq_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_hash_cb->hash_update(pcpu_shash,
+ (u8 *)per_cpu_ptr(lrng_irq_array, cpu),
+ LRNG_DATA_ARRAY_SIZE * sizeof(u32)) ?:
+ /* ... get the per-CPU pool digest, ... */
+ pcpu_hash_cb->hash_final(pcpu_shash, digest) ?:
+ /* ... re-initialize the hash, ... */
+ pcpu_hash_cb->hash_init(pcpu_shash, pcpu_hash) ?:
+ /* ... feed the old hash into the new state. */
+ pcpu_hash_cb->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.
+ *
+ * @eb: entropy buffer to store entropy
+ * @requested_bits: Requested amount of entropy
+ * @fully_seeded: indicator whether LRNG is fully seeded
+ */
+static void lrng_irq_pool_hash(struct entropy_buf *eb, u32 requested_bits,
+ bool fully_seeded)
+{
+ SHASH_DESC_ON_STACK(shash, NULL);
+ const struct lrng_hash_cb *hash_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;
+
+ /* Only deliver entropy when SP800-90B self test is completed */
+ if (!lrng_sp80090b_startup_complete_es(lrng_int_es_irq)) {
+ eb->e_bits[lrng_int_es_irq] = 0;
+ return;
+ }
+
+ /* Lock guarding replacement of per-NUMA hash */
+ read_lock_irqsave(&drng->hash_lock, flags);
+
+ hash_cb = drng->hash_cb;
+ hash = drng->hash;
+
+ /* The hash state of filled with all per-CPU pool hashes. */
+ ret = hash_cb->hash_init(shash, hash);
+ if (ret)
+ goto err;
+
+ /* Cap to maximum entropy that can ever be generated with given hash */
+ lrng_cap_requested(hash_cb->hash_digestsize(hash) << 3, requested_bits);
+ requested_irqs = lrng_entropy_to_data(requested_bits +
+ lrng_compress_osr(),
+ lrng_irq_entropy_bits);
+
+ /*
+ * 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_irq_pool_online(cpu))
+ continue;
+
+ if (lrng_drng && lrng_drng[node])
+ pcpu_drng = lrng_drng[node];
+
+ if (pcpu_drng == drng) {
+ found_irqs = lrng_irq_pool_hash_one(hash_cb, hash,
+ cpu, digest,
+ &digestsize);
+ } else {
+ read_lock_irqsave(&pcpu_drng->hash_lock, flags2);
+ found_irqs =
+ lrng_irq_pool_hash_one(pcpu_drng->hash_cb,
+ pcpu_drng->hash, cpu,
+ digest, &digestsize);
+ read_unlock_irqrestore(&pcpu_drng->hash_lock, flags2);
+ }
+
+ /* Inject the digest into the state of all per-CPU pools */
+ ret = hash_cb->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_irq_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 = hash_cb->hash_final(shash, digest);
+ if (ret)
+ goto err;
+
+ collected_ent_bits = lrng_data_to_entropy(collected_irqs,
+ lrng_irq_entropy_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 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(eb->e[lrng_int_es_irq], digest,
+ fully_seeded ? returned_ent_bits >> 3 : requested_bits >> 3);
+ eb->e_bits[lrng_int_es_irq] = returned_ent_bits;
+
+out:
+ hash_cb->hash_desc_zero(shash);
+ read_unlock_irqrestore(&drng->hash_lock, flags);
+ memzero_explicit(digest, sizeof(digest));
+ return;
+
+err:
+ eb->e_bits[lrng_int_es_irq] = 0;
+ goto out;
+}
+
+/* Compress the lrng_irq_array array into lrng_irq_pool */
+static void lrng_irq_array_compress(void)
+{
+ struct shash_desc *shash =
+ (struct shash_desc *)this_cpu_ptr(lrng_irq_pool);
+ struct lrng_drng *drng = lrng_drng_node_instance();
+ const struct lrng_hash_cb *hash_cb;
+ spinlock_t *lock = this_cpu_ptr(&lrng_irq_lock);
+ unsigned long flags, flags2;
+ void *hash;
+ bool init = false;
+
+ read_lock_irqsave(&drng->hash_lock, flags);
+ hash_cb = drng->hash_cb;
+ hash = drng->hash;
+
+ if (unlikely(!this_cpu_read(lrng_irq_lock_init))) {
+ init = true;
+ spin_lock_init(lock);
+ this_cpu_write(lrng_irq_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(), numa_node_id(),
+ hash_cb->hash_name());
+ }
+
+ spin_lock_irqsave(lock, flags2);
+
+ if (unlikely(init) && hash_cb->hash_init(shash, hash)) {
+ this_cpu_write(lrng_irq_lock_init, false);
+ pr_warn("Initialization of hash failed\n");
+ } else if (lrng_irq_continuous_compression) {
+ /* Add entire per-CPU data array content into entropy pool. */
+ if (hash_cb->hash_update(shash,
+ (u8 *)this_cpu_ptr(lrng_irq_array),
+ LRNG_DATA_ARRAY_SIZE * sizeof(u32)))
+ pr_warn_ratelimited("Hashing of entropy data failed\n");
+ }
+
+ spin_unlock_irqrestore(lock, flags2);
+ read_unlock_irqrestore(&drng->hash_lock, flags);
+}
+
+/* Compress data array into hash */
+static void lrng_irq_array_to_hash(u32 ptr)
+{
+ u32 *array = this_cpu_ptr(lrng_irq_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_irq_array_irqs), 0);
+
+ for (i = 1; i < LRNG_DATA_ARRAY_SIZE; i++)
+ lrng_raw_array_entropy_store(*(array + i));
+ } else {
+ lrng_irq_array_compress();
+ /* Ping pool handler about received entropy */
+ if (lrng_sp80090b_startup_complete_es(lrng_int_es_irq))
+ lrng_es_add_entropy();
+ }
+}
+
+/*
+ * Concatenate full 32 bit word at the end of time array even when current
+ * ptr is not aligned to sizeof(data).
+ */
+static void _lrng_irq_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_irq_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_data_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_irq_array[pre_array], ~(0xffffffff & ~mask));
+ this_cpu_or(lrng_irq_array[pre_array], data & ~mask);
+
+ /* Invoke compression as we just filled data array completely */
+ if (unlikely(pre_ptr > ptr))
+ lrng_irq_array_to_hash(LRNG_DATA_WORD_MASK);
+
+ /* LSB of data go into current unit */
+ this_cpu_write(lrng_irq_array[lrng_data_idx2array(ptr)],
+ data & mask);
+
+ if (likely(pre_ptr <= ptr))
+ lrng_irq_array_to_hash(ptr);
+}
+
+/* Concatenate a 32-bit word at the end of the per-CPU array */
+void lrng_irq_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_irq_continuous_compression)
+ _lrng_irq_array_add_u32(data);
+}
+
+/* Concatenate data of max LRNG_DATA_SLOTSIZE_MASK at the end of time array */
+static void lrng_irq_array_add_slot(u32 data)
+{
+ /* Get slot */
+ u32 ptr = this_cpu_inc_return(lrng_irq_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_irq_array[0]) << 3);
+
+ /* zeroization of slot to ensure the following OR adds the data */
+ this_cpu_and(lrng_irq_array[array],
+ ~(lrng_data_slot_val(0xffffffff & LRNG_DATA_SLOTSIZE_MASK,
+ slot)));
+ /* Store data into slot */
+ this_cpu_or(lrng_irq_array[array], lrng_data_slot_val(data, slot));
+
+ lrng_irq_array_to_hash(ptr);
+}
+
+static 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, lrng_int_es_irq);
+ if (health_test > lrng_health_fail_use)
+ return;
+
+ if (health_test == lrng_health_pass)
+ atomic_inc_return(this_cpu_ptr(&lrng_irq_array_irqs));
+
+ add_time(time);
+}
+
+/*
+ * Batching up of entropy in per-CPU array before injecting into entropy pool.
+ */
+static 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_irq_array_add_u32);
+ lrng_gcd_add_value(now_time);
+ } else {
+ /* GCD is known and applied */
+ lrng_time_process_common((now_time / lrng_gcd_get()) &
+ LRNG_DATA_SLOTSIZE_MASK,
+ lrng_irq_array_add_slot);
+ }
+
+ lrng_perf_time(now_time);
+}
+
+/* Hot code path - Callback for interrupt handler */
+void add_interrupt_randomness(int irq)
+{
+ if (lrng_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_irq_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_retip_entropy_store(ip) ? 0 : ip;
+ tmp ^= ip >> 32;
+ _lrng_irq_array_add_u32(tmp);
+ }
+}
+EXPORT_SYMBOL(add_interrupt_randomness);
+
+static 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,
+ " Hash for operating entropy pool: %s\n"
+ " Available entropy: %u\n"
+ " per-CPU interrupt collection size: %u\n"
+ " Standards compliance: %s\n"
+ " High-resolution timer: %s\n"
+ " Continuous compression: %s\n"
+ " Health test passed: %s\n",
+ lrng_drng_init->hash_cb->hash_name(),
+ lrng_irq_avail_entropy(0),
+ LRNG_DATA_NUM_VALUES,
+ lrng_sp80090b_compliant(lrng_int_es_irq) ? "SP800-90B " : "",
+ lrng_highres_timer() ? "true" : "false",
+ lrng_irq_continuous_compression ? "true" : "false",
+ lrng_sp80090b_startup_complete_es(lrng_int_es_irq) ? "true" :
+ "false");
+}
+
+struct lrng_es_cb lrng_es_irq = {
+ .name = "IRQ",
+ .get_ent = lrng_irq_pool_hash,
+ .curr_entropy = lrng_irq_avail_entropy,
+ .max_entropy = lrng_irq_avail_pool_size,
+ .state = lrng_irq_es_state,
+ .reset = lrng_irq_reset,
+ .switch_hash = lrng_irq_switch_hash,
+};
From 3f214277bf0fc706f883719b5f56a25955487e9c Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Sun, 15 May 2022 16:39:02 +0200
Subject: [PATCH 12/25] scheduler - add entropy sampling hook
The scheduler can be used as a source of entropy. This requires the
presence of a hook that invokes the entropy source implementation.
When the scheduler-based entropy source is not compiled, the hook is
folded into a noop which does not affect the scheduler in any way.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
kernel/sched/core.c | 3 +++
1 file changed, 3 insertions(+)
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -7,6 +7,7 @@
* Copyright (C) 1991-2002 Linus Torvalds
* Copyright (C) 1998-2024 Ingo Molnar, Red Hat
*/
+#include <linux/lrng.h>
#include <linux/highmem.h>
#include <linux/hrtimer_api.h>
#include <linux/ktime_api.h>
@@ -3515,6 +3516,8 @@ ttwu_stat(struct task_struct *p, int cpu
{
struct rq *rq;
+ add_sched_randomness(p, cpu);
+
if (!schedstat_enabled())
return;
From c83a2b55d00ccf38e9034664245a286ce5792be7 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Wed, 22 Feb 2023 07:05:59 +0100
Subject: [PATCH 13/25] LRNG - add scheduler-based entropy source
The scheduler-based entropy source (ES) consumes the events triggered by
the kernel invoked with the add_sched_randomness. Its main goal is:
- to be extremely fast in the scheduler context - This is guaranteed by
only concatenating the least significant bits of a time stamp into
CPU-local entropy pools. Thus, the operation is quasi-lockless. Also,
the concatenation is a very trivial operation. Finally, by discarding
the high-order bits, attacker-observable timing values are discarded.
- to use only cryptographic primitives for compression.
The scheduler entropy pool collects noise data from context-switch
timing. Any data received by the LRNG from the interrupt noise sources
is inserted into a per-CPU entropy pool using a concatenation operation.
The following processing concept is applied
(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) Only in process context when a reseed of the DRNG is requested,
the compression of the entropy pool data is performed using a hash.
When the entropy pool is full (i.e. sufficient scheduling event data
is received and yet no compression is performed), the oldest entropy
pool entries are overwritten with the current entry. Thus, the entropy
pool acts as a ring buffer.
To speed up the scheduling operation 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.
Considering the possibility that IRQ events cause at the same time
scheduling events (e.g. the IRQ tasklet is executed), only one of those
two entropy sources can ever be configured to deliver entropy. The
respective other ES may deliver data, but never increases the entropy
estimator of the LRNG.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 122 +++----
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_es_sched.c | 566 ++++++++++++++++++++++++++++++
drivers/char/lrng/lrng_health.h | 42 +++
drivers/char/lrng/lrng_testing.h | 85 +++++
5 files changed, 755 insertions(+), 61 deletions(-)
create mode 100644 drivers/char/lrng/lrng_es_sched.c
create mode 100644 drivers/char/lrng/lrng_health.h
create mode 100644 drivers/char/lrng/lrng_testing.h
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -141,9 +141,9 @@ comment "Common Timer-based Entropy Sour
config LRNG_IRQ_DFLT_TIMER_ES
bool
-# config LRNG_SCHED_DFLT_TIMER_ES
-# bool
-#
+config LRNG_SCHED_DFLT_TIMER_ES
+ bool
+
config LRNG_TIMER_COMMON
bool
@@ -164,12 +164,12 @@ choice
The interrupt entropy source is selected as a timer-based
entropy source to provide entropy.
-# config LRNG_SCHED_DFLT_TIMER_ES
-# bool "Scheduler Entropy Source"
-# depends on LRNG_SCHED
-# help
-# The scheduler entropy source is selected as timer-based
-# entropy source to provide entropy.
+ config LRNG_SCHED_DFLT_TIMER_ES
+ bool "Scheduler Entropy Source"
+ depends on LRNG_SCHED
+ help
+ The scheduler entropy source is selected as timer-based
+ entropy source to provide entropy.
endchoice
choice
@@ -534,58 +534,58 @@ config LRNG_IRQ_ENTROPY_RATE
# Note, this option is overwritten when the option
# CONFIG_RANDOM_TRUST_CPU is set.
#
-# comment "Scheduler Entropy Source"
-#
-# config LRNG_SCHED
-# bool "Enable Scheduer Entropy Source as LRNG Seed Source"
-# select LRNG_TIMER_COMMON
-# help
-# The LRNG models an entropy source based on the timing of the
-# occurrence of scheduler-triggered context switches. Enable
-# this option to enable this scheduler entropy source.
-#
-# The scheduler entropy source is triggered every time a
-# context switch is triggered thus causes the scheduler to
-# execute slightly longer. Disabling the scheduler entropy
-# source implies that the performance penalty on the scheduler
-# added by the LRNG is eliminated. Yet, this entropy source is
-# considered to be an 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 scheduler 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.
-#
-# config LRNG_SCHED_ENTROPY_RATE
-# int "Scheduler Entropy Source Entropy Rate"
-# depends on LRNG_SCHED
-# range 256 4294967295 if LRNG_SCHED_DFLT_TIMER_ES
-# range 4294967295 4294967295 if !LRNG_SCHED_DFLT_TIMER_ES
-# default 256 if LRNG_SCHED_DFLT_TIMER_ES
-# default 4294967295 if !LRNG_SCHED_DFLT_TIMER_ES
-# help
-# The LRNG will collect the configured number of context switches
-# triggered by the scheduler 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 scheduler entropy source,
-# the option has to be set to 4294967295. In this case, the
-# scheduler entropy source will still deliver data but without
-# being credited with entropy.
-#
+comment "Scheduler Entropy Source"
+
+config LRNG_SCHED
+ bool "Enable Scheduer Entropy Source as LRNG Seed Source"
+ select LRNG_TIMER_COMMON
+ help
+ The LRNG models an entropy source based on the timing of the
+ occurrence of scheduler-triggered context switches. Enable
+ this option to enable this scheduler entropy source.
+
+ The scheduler entropy source is triggered every time a
+ context switch is triggered thus causes the scheduler to
+ execute slightly longer. Disabling the scheduler entropy
+ source implies that the performance penalty on the scheduler
+ added by the LRNG is eliminated. Yet, this entropy source is
+ considered to be an 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 scheduler 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.
+
+config LRNG_SCHED_ENTROPY_RATE
+ int "Scheduler Entropy Source Entropy Rate"
+ depends on LRNG_SCHED
+ range 256 4294967295 if LRNG_SCHED_DFLT_TIMER_ES
+ range 4294967295 4294967295 if !LRNG_SCHED_DFLT_TIMER_ES
+ default 256 if LRNG_SCHED_DFLT_TIMER_ES
+ default 4294967295 if !LRNG_SCHED_DFLT_TIMER_ES
+ help
+ The LRNG will collect the configured number of context switches
+ triggered by the scheduler 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 scheduler entropy source,
+ the option has to be set to 4294967295. In this case, the
+ scheduler entropy source will still deliver data but without
+ being credited with entropy.
+
# comment "Kernel RNG Entropy Source"
#
# config LRNG_KERNEL_RNG
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -20,3 +20,4 @@ obj-$(CONFIG_LRNG_DRNG_ATOMIC) += lrng_
obj-$(CONFIG_LRNG_TIMER_COMMON) += lrng_es_timer_common.o
obj-$(CONFIG_LRNG_IRQ) += lrng_es_irq.o
+obj-$(CONFIG_LRNG_SCHED) += lrng_es_sched.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_sched.c
@@ -0,0 +1,566 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG Slow Entropy Source: Scheduler-based data collection
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <asm/irq_regs.h>
+#include <asm/ptrace.h>
+#include <linux/lrng.h>
+#include <crypto/hash.h>
+#include <linux/module.h>
+#include <linux/random.h>
+
+#include "lrng_es_aux.h"
+#include "lrng_es_sched.h"
+#include "lrng_es_timer_common.h"
+#include "lrng_health.h"
+#include "lrng_numa.h"
+#include "lrng_testing.h"
+
+/*
+ * Number of scheduler-based context switches 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_SCHED_ENTROPY_BITS \
+ LRNG_UINT32_C(CONFIG_LRNG_SCHED_ENTROPY_RATE)
+
+/* Number of events required for LRNG_DRNG_SECURITY_STRENGTH_BITS entropy */
+static u32 lrng_sched_entropy_bits = LRNG_SCHED_ENTROPY_BITS;
+
+static u32 sched_entropy __read_mostly = LRNG_SCHED_ENTROPY_BITS;
+#ifdef CONFIG_LRNG_RUNTIME_ES_CONFIG
+module_param(sched_entropy, uint, 0444);
+MODULE_PARM_DESC(sched_entropy,
+ "How many scheduler-based context switches 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_sched_array)
+ __aligned(LRNG_KCAPI_ALIGN);
+static DEFINE_PER_CPU(u32, lrng_sched_array_ptr) = 0;
+static DEFINE_PER_CPU(atomic_t, lrng_sched_array_events) = ATOMIC_INIT(0);
+
+/*
+ * 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_sched_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_sched_lock);
+static DEFINE_PER_CPU(bool, lrng_sched_lock_init) = false;
+
+static bool lrng_sched_pool_online(int cpu)
+{
+ return per_cpu(lrng_sched_lock_init, cpu);
+}
+
+static void __init lrng_sched_check_compression_state(void)
+{
+ /* One pool should hold sufficient entropy for disabled compression */
+ u32 max_ent = min_t(u32, lrng_get_digestsize(),
+ lrng_data_to_entropy(LRNG_DATA_NUM_VALUES,
+ lrng_sched_entropy_bits));
+ if (max_ent < lrng_security_strength()) {
+ pr_devel("Scheduler entropy source will never provide %u bits of entropy required for fully seeding the DRNG all by itself\n",
+ lrng_security_strength());
+ }
+}
+
+void __init lrng_sched_es_init(bool highres_timer)
+{
+ /* Set a minimum number of scheduler events that must be collected */
+ sched_entropy = max_t(u32, LRNG_SCHED_ENTROPY_BITS, sched_entropy);
+
+ if (highres_timer) {
+ lrng_sched_entropy_bits = sched_entropy;
+ } else {
+ u32 new_entropy = sched_entropy * LRNG_ES_OVERSAMPLING_FACTOR;
+
+ lrng_sched_entropy_bits = (sched_entropy < new_entropy) ?
+ new_entropy : sched_entropy;
+ pr_warn("operating without high-resolution timer and applying oversampling factor %u\n",
+ LRNG_ES_OVERSAMPLING_FACTOR);
+ }
+
+ lrng_sched_check_compression_state();
+}
+
+static u32 lrng_sched_avail_pool_size(void)
+{
+ u32 max_pool = lrng_get_digestsize(),
+ max_size = min_t(u32, max_pool, LRNG_DATA_NUM_VALUES);
+ int cpu;
+
+ for_each_online_cpu(cpu)
+ max_size += max_pool;
+
+ return max_size;
+}
+
+/* Return entropy of unused scheduler events present in all per-CPU pools. */
+static u32 lrng_sched_avail_entropy(u32 __unused)
+{
+ u32 digestsize_events, events = 0;
+ int cpu;
+
+ /* Only deliver entropy when SP800-90B self test is completed */
+ if (!lrng_sp80090b_startup_complete_es(lrng_int_es_sched))
+ return 0;
+
+ /* Obtain the cap of maximum numbers of scheduler events we count */
+ digestsize_events = lrng_entropy_to_data(lrng_get_digestsize(),
+ lrng_sched_entropy_bits);
+ /* Cap to max. number of scheduler events the array can hold */
+ digestsize_events = min_t(u32, digestsize_events, LRNG_DATA_NUM_VALUES);
+
+ for_each_online_cpu(cpu) {
+ events += min_t(u32, digestsize_events,
+ atomic_read_u32(per_cpu_ptr(&lrng_sched_array_events,
+ cpu)));
+ }
+
+ /* Consider oversampling rate */
+ return lrng_reduce_by_osr(
+ lrng_data_to_entropy(events, lrng_sched_entropy_bits));
+}
+
+/*
+ * Reset all per-CPU pools - reset entropy estimator but leave the pool data
+ * that may or may not have entropy unchanged.
+ */
+static void lrng_sched_reset(void)
+{
+ int cpu;
+
+ /* Trigger GCD calculation anew. */
+ lrng_gcd_set(0);
+
+ for_each_online_cpu(cpu)
+ atomic_set(per_cpu_ptr(&lrng_sched_array_events, cpu), 0);
+}
+
+/*
+ * 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).
+ */
+static int
+lrng_sched_switch_hash(struct lrng_drng *drng, int node,
+ const struct lrng_hash_cb *new_cb, void *new_hash,
+ const struct lrng_hash_cb *old_cb)
+{
+ u8 digest[LRNG_MAX_DIGESTSIZE];
+ u32 digestsize_events, found_events;
+ int ret = 0, cpu;
+
+ if (!IS_ENABLED(CONFIG_LRNG_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 hash_cb only applies NUMA-node-wide.
+ */
+ if (cpu_to_node(cpu) != node || !lrng_sched_pool_online(cpu))
+ continue;
+
+ pcpu_shash = (struct shash_desc *)per_cpu_ptr(lrng_sched_pool,
+ cpu);
+
+ digestsize_events = old_cb->hash_digestsize(pcpu_shash);
+ digestsize_events = lrng_entropy_to_data(digestsize_events << 3,
+ lrng_sched_entropy_bits);
+
+ if (pcpu_shash->tfm == new_hash)
+ continue;
+
+ /* Get the per-CPU pool hash with old digest ... */
+ ret = old_cb->hash_final(pcpu_shash, digest) ?:
+ /* ... re-initialize the hash with the new digest ... */
+ new_cb->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->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_events = atomic_xchg_relaxed(
+ per_cpu_ptr(&lrng_sched_array_events, cpu), 0);
+ found_events = min_t(u32, found_events, digestsize_events);
+ atomic_add_return_relaxed(found_events,
+ per_cpu_ptr(&lrng_sched_array_events, cpu));
+
+ pr_debug("Re-initialize per-CPU scheduler entropy pool for CPU %d on NUMA node %d with hash %s\n",
+ cpu, node, new_cb->hash_name());
+ }
+
+out:
+ memzero_explicit(digest, sizeof(digest));
+ return ret;
+}
+
+static u32
+lrng_sched_pool_hash_one(const struct lrng_hash_cb *pcpu_hash_cb,
+ void *pcpu_hash, int cpu, u8 *digest, u32 *digestsize)
+{
+ struct shash_desc *pcpu_shash =
+ (struct shash_desc *)per_cpu_ptr(lrng_sched_pool, cpu);
+ spinlock_t *lock = per_cpu_ptr(&lrng_sched_lock, cpu);
+ unsigned long flags;
+ u32 digestsize_events, found_events;
+
+ if (unlikely(!per_cpu(lrng_sched_lock_init, cpu))) {
+ if (pcpu_hash_cb->hash_init(pcpu_shash, pcpu_hash)) {
+ pr_warn("Initialization of hash failed\n");
+ return 0;
+ }
+ spin_lock_init(lock);
+ per_cpu(lrng_sched_lock_init, cpu) = true;
+ pr_debug("Initializing per-CPU scheduler entropy pool for CPU %d with hash %s\n",
+ raw_smp_processor_id(), pcpu_hash_cb->hash_name());
+ }
+
+ /* Lock guarding against reading / writing to per-CPU pool */
+ spin_lock_irqsave(lock, flags);
+
+ *digestsize = pcpu_hash_cb->hash_digestsize(pcpu_hash);
+ digestsize_events = lrng_entropy_to_data(*digestsize << 3,
+ lrng_sched_entropy_bits);
+
+ /* Obtain entropy statement like for the entropy pool */
+ found_events = atomic_xchg_relaxed(
+ per_cpu_ptr(&lrng_sched_array_events, cpu), 0);
+ /* Cap to maximum amount of data we can hold in hash */
+ found_events = min_t(u32, found_events, digestsize_events);
+
+ /* Cap to maximum amount of data we can hold in array */
+ found_events = min_t(u32, found_events, LRNG_DATA_NUM_VALUES);
+
+ /* Store all not-yet compressed data in data array into hash, ... */
+ if (pcpu_hash_cb->hash_update(pcpu_shash,
+ (u8 *)per_cpu_ptr(lrng_sched_array, cpu),
+ LRNG_DATA_ARRAY_SIZE * sizeof(u32)) ?:
+ /* ... get the per-CPU pool digest, ... */
+ pcpu_hash_cb->hash_final(pcpu_shash, digest) ?:
+ /* ... re-initialize the hash, ... */
+ pcpu_hash_cb->hash_init(pcpu_shash, pcpu_hash) ?:
+ /* ... feed the old hash into the new state. */
+ pcpu_hash_cb->hash_update(pcpu_shash, digest, *digestsize))
+ found_events = 0;
+
+ spin_unlock_irqrestore(lock, flags);
+ return found_events;
+}
+
+/*
+ * Hash all per-CPU arrays 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 scheduler
+ * events into an entropy statement. The conversion depends on
+ * LRNG_SCHED_ENTROPY_BITS which defines how many scheduler events must be
+ * received to obtain 256 bits of entropy. With this value, the function
+ * lrng_data_to_entropy converts a given data size (received scheduler events,
+ * requested amount of data, etc.) into an entropy statement.
+ * lrng_entropy_to_data does the reverse.
+ *
+ * @eb: entropy buffer to store entropy
+ * @requested_bits: Requested amount of entropy
+ * @fully_seeded: indicator whether LRNG is fully seeded
+ */
+static void lrng_sched_pool_hash(struct entropy_buf *eb, u32 requested_bits,
+ bool fully_seeded)
+{
+ SHASH_DESC_ON_STACK(shash, NULL);
+ const struct lrng_hash_cb *hash_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_events, collected_events = 0, collected_ent_bits,
+ requested_events, returned_ent_bits;
+ int ret, cpu;
+ void *hash;
+
+ /* Only deliver entropy when SP800-90B self test is completed */
+ if (!lrng_sp80090b_startup_complete_es(lrng_int_es_sched)) {
+ eb->e_bits[lrng_int_es_sched] = 0;
+ return;
+ }
+
+ /* Lock guarding replacement of per-NUMA hash */
+ read_lock_irqsave(&drng->hash_lock, flags);
+
+ hash_cb = drng->hash_cb;
+ hash = drng->hash;
+
+ /* The hash state of filled with all per-CPU pool hashes. */
+ ret = hash_cb->hash_init(shash, hash);
+ if (ret)
+ goto err;
+
+ /* Cap to maximum entropy that can ever be generated with given hash */
+ lrng_cap_requested(hash_cb->hash_digestsize(hash) << 3, requested_bits);
+ requested_events = lrng_entropy_to_data(requested_bits +
+ lrng_compress_osr(),
+ lrng_sched_entropy_bits);
+
+ /*
+ * 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, unused_events = 0;
+ int node = cpu_to_node(cpu);
+
+ if (lrng_drng && lrng_drng[node])
+ pcpu_drng = lrng_drng[node];
+
+ if (pcpu_drng == drng) {
+ found_events = lrng_sched_pool_hash_one(hash_cb, hash,
+ cpu, digest,
+ &digestsize);
+ } else {
+ read_lock_irqsave(&pcpu_drng->hash_lock, flags2);
+ found_events =
+ lrng_sched_pool_hash_one(pcpu_drng->hash_cb,
+ pcpu_drng->hash, cpu,
+ digest, &digestsize);
+ read_unlock_irqrestore(&pcpu_drng->hash_lock, flags2);
+ }
+
+ /* Store all not-yet compressed data in data array into hash */
+ ret = hash_cb->hash_update(shash, digest, digestsize);
+ if (ret)
+ goto err;
+
+ collected_events += found_events;
+ if (collected_events > requested_events) {
+ unused_events = collected_events - requested_events;
+ atomic_add_return_relaxed(unused_events,
+ per_cpu_ptr(&lrng_sched_array_events, cpu));
+ collected_events = requested_events;
+ }
+ pr_debug("%u scheduler-based events used from entropy array of CPU %d, %u scheduler-based events remain unused\n",
+ found_events - unused_events, cpu, unused_events);
+ }
+
+ ret = hash_cb->hash_final(shash, digest);
+ if (ret)
+ goto err;
+
+ collected_ent_bits = lrng_data_to_entropy(collected_events,
+ lrng_sched_entropy_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 scheduler-based 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(eb->e[lrng_int_es_sched], digest,
+ fully_seeded ? returned_ent_bits >> 3 : requested_bits >> 3);
+ eb->e_bits[lrng_int_es_sched] = returned_ent_bits;
+
+out:
+ hash_cb->hash_desc_zero(shash);
+ read_unlock_irqrestore(&drng->hash_lock, flags);
+ memzero_explicit(digest, sizeof(digest));
+ return;
+
+err:
+ eb->e_bits[lrng_int_es_sched] = 0;
+ goto out;
+}
+
+/*
+ * Concatenate full 32 bit word at the end of time array even when current
+ * ptr is not aligned to sizeof(data).
+ */
+static void lrng_sched_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_sched_array_ptr,
+ LRNG_DATA_SLOTS_PER_UINT);
+ unsigned int pre_array;
+
+ lrng_data_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_sched_array[pre_array], ~(0xffffffff & ~mask));
+ this_cpu_or(lrng_sched_array[pre_array], data & ~mask);
+
+ /*
+ * Continuous compression is not allowed for scheduler noise source,
+ * so do not call lrng_sched_array_to_hash here.
+ */
+
+ /* LSB of data go into current unit */
+ this_cpu_write(lrng_sched_array[lrng_data_idx2array(ptr)],
+ data & mask);
+}
+
+/* Concatenate data of max LRNG_DATA_SLOTSIZE_MASK at the end of time array */
+static void lrng_sched_array_add_slot(u32 data)
+{
+ /* Get slot */
+ u32 ptr = this_cpu_inc_return(lrng_sched_array_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 */
+ this_cpu_and(lrng_sched_array[array],
+ ~(lrng_data_slot_val(0xffffffff & LRNG_DATA_SLOTSIZE_MASK,
+ slot)));
+ /* Store data into slot */
+ this_cpu_or(lrng_sched_array[array], lrng_data_slot_val(data, slot));
+
+ /*
+ * Continuous compression is not allowed for scheduler noise source,
+ * so do not call lrng_sched_array_to_hash here.
+ */
+}
+
+static void
+lrng_time_process_common(u32 time, void(*add_time)(u32 data))
+{
+ enum lrng_health_res health_test;
+
+ if (lrng_raw_sched_hires_entropy_store(time))
+ return;
+
+ health_test = lrng_health_test(time, lrng_int_es_sched);
+ if (health_test > lrng_health_fail_use)
+ return;
+
+ if (health_test == lrng_health_pass)
+ atomic_inc_return(this_cpu_ptr(&lrng_sched_array_events));
+
+ add_time(time);
+
+ /*
+ * We cannot call lrng_es_add_entropy() as this would call a schedule
+ * operation that is not permissible in scheduler context.
+ * As the scheduler ES provides a high bandwidth of entropy, we assume
+ * that other reseed triggers happen to pick up the scheduler ES
+ * entropy in due time.
+ */
+}
+
+/* Batching up of entropy in per-CPU array */
+static void lrng_sched_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_sched_array_add_u32);
+ lrng_gcd_add_value(now_time);
+ } else {
+ /* GCD is known and applied */
+ lrng_time_process_common((now_time / lrng_gcd_get()) &
+ LRNG_DATA_SLOTSIZE_MASK,
+ lrng_sched_array_add_slot);
+ }
+
+ lrng_sched_perf_time(now_time);
+}
+
+void add_sched_randomness(const struct task_struct *p, int cpu)
+{
+ if (lrng_highres_timer()) {
+ lrng_sched_time_process();
+ } else {
+ u32 tmp = cpu;
+
+ tmp ^= lrng_raw_sched_pid_entropy_store(p->pid) ?
+ 0 : (u32)p->pid;
+ tmp ^= lrng_raw_sched_starttime_entropy_store(p->start_time) ?
+ 0 : (u32)p->start_time;
+ tmp ^= lrng_raw_sched_nvcsw_entropy_store(p->nvcsw) ?
+ 0 : (u32)p->nvcsw;
+
+ lrng_sched_time_process();
+ lrng_sched_array_add_u32(tmp);
+ }
+}
+EXPORT_SYMBOL(add_sched_randomness);
+
+static void lrng_sched_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,
+ " Hash for operating entropy pool: %s\n"
+ " Available entropy: %u\n"
+ " per-CPU scheduler event collection size: %u\n"
+ " Standards compliance: %s\n"
+ " High-resolution timer: %s\n"
+ " Health test passed: %s\n",
+ lrng_drng_init->hash_cb->hash_name(),
+ lrng_sched_avail_entropy(0),
+ LRNG_DATA_NUM_VALUES,
+ lrng_sp80090b_compliant(lrng_int_es_sched) ? "SP800-90B " : "",
+ lrng_highres_timer() ? "true" : "false",
+ lrng_sp80090b_startup_complete_es(lrng_int_es_sched) ?
+ "true" :
+ "false");
+}
+
+struct lrng_es_cb lrng_es_sched = {
+ .name = "Scheduler",
+ .get_ent = lrng_sched_pool_hash,
+ .curr_entropy = lrng_sched_avail_entropy,
+ .max_entropy = lrng_sched_avail_pool_size,
+ .state = lrng_sched_es_state,
+ .reset = lrng_sched_reset,
+ .switch_hash = lrng_sched_switch_hash,
+};
--- /dev/null
+++ b/drivers/char/lrng/lrng_health.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_HEALTH_H
+#define _LRNG_HEALTH_H
+
+#include "lrng_es_mgr.h"
+
+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_es(enum lrng_internal_es es);
+bool lrng_sp80090b_compliant(enum lrng_internal_es es);
+
+enum lrng_health_res lrng_health_test(u32 now_time, enum lrng_internal_es es);
+void lrng_health_disable(void);
+#else /* CONFIG_LRNG_HEALTH_TESTS */
+static inline bool lrng_sp80090b_startup_complete_es(enum lrng_internal_es es)
+{
+ return true;
+}
+
+static inline bool lrng_sp80090b_compliant(enum lrng_internal_es es)
+{
+ return false;
+}
+
+static inline enum lrng_health_res
+lrng_health_test(u32 now_time, enum lrng_internal_es es)
+{
+ return lrng_health_pass;
+}
+static inline void lrng_health_disable(void) { }
+#endif /* CONFIG_LRNG_HEALTH_TESTS */
+
+#endif /* _LRNG_HEALTH_H */
--- /dev/null
+++ b/drivers/char/lrng/lrng_testing.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#ifndef _LRNG_TESTING_H
+#define _LRNG_TESTING_H
+
+#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_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 */
+
+#ifdef CONFIG_LRNG_RAW_SCHED_HIRES_ENTROPY
+bool lrng_raw_sched_hires_entropy_store(u32 value);
+#else /* CONFIG_LRNG_RAW_SCHED_HIRES_ENTROPY */
+static inline bool
+lrng_raw_sched_hires_entropy_store(u32 value) { return false; }
+#endif /* CONFIG_LRNG_RAW_SCHED_HIRES_ENTROPY */
+
+#ifdef CONFIG_LRNG_RAW_SCHED_PID_ENTROPY
+bool lrng_raw_sched_pid_entropy_store(u32 value);
+#else /* CONFIG_LRNG_RAW_SCHED_PID_ENTROPY */
+static inline bool
+lrng_raw_sched_pid_entropy_store(u32 value) { return false; }
+#endif /* CONFIG_LRNG_RAW_SCHED_PID_ENTROPY */
+
+#ifdef CONFIG_LRNG_RAW_SCHED_START_TIME_ENTROPY
+bool lrng_raw_sched_starttime_entropy_store(u32 value);
+#else /* CONFIG_LRNG_RAW_SCHED_START_TIME_ENTROPY */
+static inline bool
+lrng_raw_sched_starttime_entropy_store(u32 value) { return false; }
+#endif /* CONFIG_LRNG_RAW_SCHED_START_TIME_ENTROPY */
+
+#ifdef CONFIG_LRNG_RAW_SCHED_NVCSW_ENTROPY
+bool lrng_raw_sched_nvcsw_entropy_store(u32 value);
+#else /* CONFIG_LRNG_RAW_SCHED_NVCSW_ENTROPY */
+static inline bool
+lrng_raw_sched_nvcsw_entropy_store(u32 value) { return false; }
+#endif /* CONFIG_LRNG_RAW_SCHED_NVCSW_ENTROPY */
+
+#ifdef CONFIG_LRNG_SCHED_PERF
+bool lrng_sched_perf_time(u32 start);
+#else /* CONFIG_LRNG_SCHED_PERF */
+static inline bool lrng_sched_perf_time(u32 start) { return false; }
+#endif /*CONFIG_LRNG_SCHED_PERF */
+
+#endif /* _LRNG_TESTING_H */
From 77e3f8edebcba3cc3b60b654fa0ef12e73f4e378 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Tue, 25 Apr 2023 23:13:30 +0200
Subject: [PATCH 14/25] LRNG - add SP800-90B compliant health tests
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Implement health tests for LRNG's internal entropy sources as mandated
by SP-800-90B. These internal entropy sources (scheduler and IRQ ES)
both rest on high-resolution time stamps as their noise source. The patch
contains the following health tests which are independently enabled and
applied for the mentioned entropy sources.:
- 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.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 140 +++++-----
drivers/char/lrng/Makefile | 2 +
drivers/char/lrng/lrng_health.c | 447 ++++++++++++++++++++++++++++++++
3 files changed, 519 insertions(+), 70 deletions(-)
create mode 100644 drivers/char/lrng/lrng_health.c
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -229,80 +229,80 @@ config LRNG_COLLECTION_SIZE
default 4096 if LRNG_COLLECTION_SIZE_4096
default 8192 if LRNG_COLLECTION_SIZE_8192
-# config LRNG_HEALTH_TESTS
-# bool "Enable internal entropy source online health tests"
-# depends on LRNG_TIMER_COMMON
-# help
-# The online health tests applied to the interrupt entropy
-# source and to the scheduler entropy source to 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 both, the scheduler and the interrupt entropy sources,
-# are enabled, the health tests for both are applied
-# independent of each other.
-#
-# 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.
-#
+config LRNG_HEALTH_TESTS
+ bool "Enable internal entropy source online health tests"
+ depends on LRNG_TIMER_COMMON
+ help
+ The online health tests applied to the interrupt entropy
+ source and to the scheduler entropy source to 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 both, the scheduler and the interrupt entropy sources,
+ are enabled, the health tests for both are applied
+ independent of each other.
+
+ 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
-#
+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.1 - significance level 2^-80
-# config LRNG_RCT_CUTOFF_PERMANENT
-# int
-# default 81 if !LRNG_RCT_BROKEN
-# default 2 if LRNG_RCT_BROKEN
-#
+config LRNG_RCT_CUTOFF_PERMANENT
+ int
+ default 81 if !LRNG_RCT_BROKEN
+ default 2 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_APT_CUTOFF
+ int
+ default 325 if !LRNG_APT_BROKEN
+ default 32 if LRNG_APT_BROKEN
+
# Default taken from SP800-90B sec 4.4.2 - significance level 2^-80
-# config LRNG_APT_CUTOFF_PERMANENT
-# int
-# default 371 if !LRNG_APT_BROKEN
-# default 33 if LRNG_APT_BROKEN
+config LRNG_APT_CUTOFF_PERMANENT
+ int
+ default 371 if !LRNG_APT_BROKEN
+ default 33 if LRNG_APT_BROKEN
comment "Interrupt Entropy Source"
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -21,3 +21,5 @@ obj-$(CONFIG_LRNG_DRNG_ATOMIC) += lrng_
obj-$(CONFIG_LRNG_TIMER_COMMON) += lrng_es_timer_common.o
obj-$(CONFIG_LRNG_IRQ) += lrng_es_irq.o
obj-$(CONFIG_LRNG_SCHED) += lrng_es_sched.o
+
+obj-$(CONFIG_LRNG_HEALTH_TESTS) += lrng_health.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_health.c
@@ -0,0 +1,447 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Entropy Source and DRNG Manager (LRNG) Health Testing
+ *
+ * Copyright (C) 2022 - 2023, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/fips.h>
+#include <linux/module.h>
+
+#include "lrng_definitions.h"
+#include "lrng_es_mgr.h"
+#include "lrng_health.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? */
+};
+
+/* Health data collected for one entropy source */
+struct lrng_health_es_state {
+ struct lrng_rct rct;
+ struct lrng_apt apt;
+
+ /* 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;
+};
+
+#define LRNG_HEALTH_ES_INIT(x) \
+ x.rct.rct_count = ATOMIC_INIT(0), \
+ x.apt.apt_count = ATOMIC_INIT(0), \
+ x.apt.apt_base = ATOMIC_INIT(-1), \
+ x.apt.apt_trigger = ATOMIC_INIT(LRNG_APT_WINDOW_SIZE), \
+ x.apt.apt_base_set = false, \
+ x.sp80090b_startup_blocks = ATOMIC_INIT(LRNG_SP80090B_STARTUP_BLOCKS), \
+ x.sp80090b_startup_done = false,
+
+/* The health test code must operate lock-less */
+struct lrng_health {
+ bool health_test_enabled;
+ struct lrng_health_es_state es_state[lrng_int_es_last];
+};
+
+static struct lrng_health lrng_health = {
+ .health_test_enabled = true,
+
+#ifdef CONFIG_LRNG_IRQ
+ LRNG_HEALTH_ES_INIT(.es_state[lrng_int_es_irq])
+#endif
+#ifdef CONFIG_LRNG_SCHED
+ LRNG_HEALTH_ES_INIT(.es_state[lrng_int_es_sched])
+#endif
+};
+
+static DEFINE_PER_CPU(struct lrng_stuck_test[lrng_int_es_last],
+ lrng_stuck_test_array);
+
+static bool lrng_sp80090b_health_requested(void)
+{
+ /* Health tests are only requested in FIPS mode */
+ return fips_enabled;
+}
+
+static 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_full
+ *
+ * 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 void lrng_sp80090b_startup(struct lrng_health *health,
+ enum lrng_internal_es es)
+{
+ struct lrng_health_es_state *es_state = &health->es_state[es];
+
+ if (!es_state->sp80090b_startup_done &&
+ atomic_dec_and_test(&es_state->sp80090b_startup_blocks)) {
+ es_state->sp80090b_startup_done = true;
+ pr_info("SP800-90B startup health tests for internal entropy source %u completed\n",
+ es);
+ lrng_drng_force_reseed();
+
+ /*
+ * We cannot call lrng_es_add_entropy() as this may cause a
+ * schedule operation while in scheduler context for the
+ * scheduler ES.
+ */
+ }
+}
+
+/*
+ * Handle failure of SP800-90B startup testing
+ */
+static void lrng_sp80090b_startup_failure(struct lrng_health *health,
+ enum lrng_internal_es es)
+{
+ struct lrng_health_es_state *es_state = &health->es_state[es];
+
+
+ /* 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(&es_state->sp80090b_startup_blocks,
+ LRNG_SP80090B_STARTUP_BLOCKS);
+}
+
+/*
+ * Handle failure of SP800-90B runtime testing
+ */
+static void lrng_sp80090b_runtime_failure(struct lrng_health *health,
+ enum lrng_internal_es es)
+{
+ struct lrng_health_es_state *es_state = &health->es_state[es];
+
+ lrng_sp80090b_startup_failure(health, es);
+ es_state->sp80090b_startup_done = false;
+}
+
+static void lrng_rct_reset(struct lrng_rct *rct);
+static void lrng_apt_reset(struct lrng_apt *apt, unsigned int time_masked);
+static void lrng_apt_restart(struct lrng_apt *apt);
+static void lrng_sp80090b_permanent_failure(struct lrng_health *health,
+ enum lrng_internal_es es)
+{
+ struct lrng_health_es_state *es_state = &health->es_state[es];
+ struct lrng_apt *apt = &es_state->apt;
+ struct lrng_rct *rct = &es_state->rct;
+
+ if (lrng_enforce_panic_on_permanent_health_failure()) {
+ panic("SP800-90B permanent health test failure for internal entropy source %u\n",
+ es);
+ }
+
+ pr_err("SP800-90B permanent health test failure for internal entropy source %u - invalidating all existing entropy and initiate SP800-90B startup\n",
+ es);
+ lrng_sp80090b_runtime_failure(health, es);
+
+ lrng_rct_reset(rct);
+ lrng_apt_reset(apt, 0);
+ lrng_apt_restart(apt);
+}
+
+static void lrng_sp80090b_failure(struct lrng_health *health,
+ enum lrng_internal_es es)
+{
+ struct lrng_health_es_state *es_state = &health->es_state[es];
+
+ if (es_state->sp80090b_startup_done) {
+ pr_warn("SP800-90B runtime health test failure for internal entropy source %u - invalidating all existing entropy and initiate SP800-90B startup\n", es);
+ lrng_sp80090b_runtime_failure(health, es);
+ } else {
+ pr_warn("SP800-90B startup test failure for internal entropy source %u - resetting\n", es);
+ lrng_sp80090b_startup_failure(health, es);
+ }
+}
+
+bool lrng_sp80090b_startup_complete_es(enum lrng_internal_es es)
+{
+ struct lrng_health *health = &lrng_health;
+ struct lrng_health_es_state *es_state = &health->es_state[es];
+
+ if (!lrng_sp80090b_health_enabled())
+ return true;
+
+ return es_state->sp80090b_startup_done;
+}
+
+bool lrng_sp80090b_compliant(enum lrng_internal_es es)
+{
+ struct lrng_health *health = &lrng_health;
+ struct lrng_health_es_state *es_state = &health->es_state[es];
+
+ return lrng_sp80090b_health_enabled() &&
+ es_state->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 void lrng_apt_reset(struct lrng_apt *apt, unsigned int time_masked)
+{
+ /* Reset APT */
+ atomic_set(&apt->apt_count, 0);
+ atomic_set(&apt->apt_base, time_masked);
+}
+
+static void lrng_apt_restart(struct lrng_apt *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 void lrng_apt_insert(struct lrng_health *health,
+ unsigned int now_time, enum lrng_internal_es es)
+{
+ struct lrng_health_es_state *es_state = &health->es_state[es];
+ struct lrng_apt *apt = &es_state->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_PERMANENT)
+ lrng_sp80090b_permanent_failure(health, es);
+ else if (apt_val >= CONFIG_LRNG_APT_CUTOFF)
+ lrng_sp80090b_failure(health, es);
+ }
+
+ if (atomic_dec_and_test(&apt->apt_trigger)) {
+ lrng_apt_restart(apt);
+ lrng_apt_reset(apt, now_time);
+ lrng_sp80090b_startup(health, es);
+ }
+}
+
+/***************************************************************************
+ * 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.
+ ***************************************************************************/
+
+static void lrng_rct_reset(struct lrng_rct *rct)
+{
+ /* Reset RCT */
+ atomic_set(&rct->rct_count, 0);
+}
+
+/*
+ * Hot code path - Insert data for Repetition Count Test
+ *
+ * @health: Reference to health information
+ * @stuck: Decision of stuck test
+ */
+static void lrng_rct(struct lrng_health *health, enum lrng_internal_es es,
+ int stuck)
+{
+ struct lrng_health_es_state *es_state = &health->es_state[es];
+ struct lrng_rct *rct = &es_state->rct;
+
+ if (!lrng_sp80090b_health_requested())
+ return;
+
+ if (stuck) {
+ u32 rct_count = atomic_add_return_relaxed(1, &rct->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_PERMANENT)
+ lrng_sp80090b_permanent_failure(health, es);
+ else if (rct_count >= CONFIG_LRNG_RCT_CUTOFF)
+ lrng_sp80090b_failure(health, es);
+ } else {
+ lrng_rct_reset(rct);
+ }
+}
+
+/***************************************************************************
+ * 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 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 int lrng_irq_stuck(enum lrng_internal_es es, u32 now_time)
+{
+ struct lrng_stuck_test *stuck = this_cpu_ptr(lrng_stuck_test_array);
+ u32 delta = lrng_delta(stuck[es].last_time, now_time);
+ u32 delta2 = lrng_delta(stuck[es].last_delta, delta);
+ u32 delta3 = lrng_delta(stuck[es].last_delta2, delta2);
+
+ stuck[es].last_time = now_time;
+ stuck[es].last_delta = delta;
+ stuck[es].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, enum lrng_internal_es es)
+{
+ struct lrng_health *health = &lrng_health;
+ int stuck;
+
+ if (!health->health_test_enabled)
+ return lrng_health_pass;
+
+ lrng_apt_insert(health, now_time, es);
+
+ stuck = lrng_irq_stuck(es, now_time);
+ lrng_rct(health, es, 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;
+}
From 92148eed7179e55a0f9d5cbedf42b814502e222a Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Mon, 20 Feb 2023 22:07:27 +0100
Subject: [PATCH 15/25] LRNG - add random.c entropy source support
The random.c implementation can be used as an entropy source by the
LRNG. This support can be enabled at compile time.
The entropy rate can be set at compile time which is only applied:
- once the random.c considers itself fully seeded, and
- the kernel does not operate in FIPS mode (i.e. fips=1 is not set at
the kernel command line)
If one of these properties is not set, the ES will obtain data, but will
credit it with zero bits of entropy. For the first bullet, it is clear
why it will have zero bits of entropy. But for the second property, this
is set because the random.c is not operating SP800-90B compliant and
thus must be treated to not deliver any entropy in FIPS mode.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 56 ++++++++---------
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_es_krng.c | 100 +++++++++++++++++++++++++++++++
3 files changed, 129 insertions(+), 28 deletions(-)
create mode 100644 drivers/char/lrng/lrng_es_krng.c
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -586,34 +586,34 @@ config LRNG_SCHED_ENTROPY_RATE
scheduler entropy source will still deliver data but without
being credited with entropy.
-# comment "Kernel RNG Entropy Source"
-#
-# config LRNG_KERNEL_RNG
-# bool "Enable Kernel RNG as LRNG Seed Source"
-# depends on RANDOM_DEFAULT_IMPL
-# help
-# The LRNG may use the kernel RNG (random.c) as entropy
-# source.
-#
-# config LRNG_KERNEL_RNG_ENTROPY_RATE
-# int "Kernel RNG Entropy Source Entropy Rate"
-# depends on LRNG_KERNEL_RNG
-# range 0 256
-# default 256
-# help
-# The option defines the amount of entropy the LRNG applies to 256
-# bits of data obtained from the kernel 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 kernel RNG entropy source
-# will provide 256 bits of data without being credited to contain
-# entropy.
-#
-# Note: This value is set to 0 automatically when booting the
-# kernel in FIPS mode (with fips=1 kernel command line option).
-# This is due to the fact that random.c is not SP800-90B
-# compliant.
+comment "Kernel RNG Entropy Source"
+
+config LRNG_KERNEL_RNG
+ bool "Enable Kernel RNG as LRNG Seed Source"
+ depends on RANDOM_DEFAULT_IMPL
+ help
+ The LRNG may use the kernel RNG (random.c) as entropy
+ source.
+
+config LRNG_KERNEL_RNG_ENTROPY_RATE
+ int "Kernel RNG Entropy Source Entropy Rate"
+ depends on LRNG_KERNEL_RNG
+ range 0 256
+ default 256
+ help
+ The option defines the amount of entropy the LRNG applies to 256
+ bits of data obtained from the kernel 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 kernel RNG entropy source
+ will provide 256 bits of data without being credited to contain
+ entropy.
+
+ Note: This value is set to 0 automatically when booting the
+ kernel in FIPS mode (with fips=1 kernel command line option).
+ This is due to the fact that random.c is not SP800-90B
+ compliant.
endmenu # "Entropy Source Configuration"
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_LRNG_DRNG_ATOMIC) += lrng_
obj-$(CONFIG_LRNG_TIMER_COMMON) += lrng_es_timer_common.o
obj-$(CONFIG_LRNG_IRQ) += lrng_es_irq.o
+obj-$(CONFIG_LRNG_KERNEL_RNG) += lrng_es_krng.o
obj-$(CONFIG_LRNG_SCHED) += lrng_es_sched.o
obj-$(CONFIG_LRNG_HEALTH_TESTS) += lrng_health.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_krng.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG Fast Entropy Source: Linux kernel RNG (random.c)
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/fips.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/types.h>
+
+#include "lrng_es_aux.h"
+#include "lrng_es_krng.h"
+
+static u32 krng_entropy = CONFIG_LRNG_KERNEL_RNG_ENTROPY_RATE;
+#ifdef CONFIG_LRNG_RUNTIME_ES_CONFIG
+module_param(krng_entropy, uint, 0644);
+MODULE_PARM_DESC(krng_entropy, "Entropy in bits of 256 data bits from the kernel RNG noise source");
+#endif
+
+static atomic_t lrng_krng_initial_rate = ATOMIC_INIT(0);
+
+static u32 lrng_krng_fips_entropylevel(u32 entropylevel)
+{
+ return fips_enabled ? 0 : entropylevel;
+}
+
+static int lrng_krng_adjust_entropy(void)
+{
+ u32 entropylevel;
+
+ krng_entropy = atomic_read_u32(&lrng_krng_initial_rate);
+
+ entropylevel = lrng_krng_fips_entropylevel(krng_entropy);
+ pr_debug("Kernel RNG is fully seeded, setting entropy rate to %u bits of entropy\n",
+ entropylevel);
+ lrng_drng_force_reseed();
+ if (entropylevel)
+ lrng_es_add_entropy();
+ return 0;
+}
+
+static u32 lrng_krng_entropylevel(u32 requested_bits)
+{
+ static bool init = false;
+
+ if (unlikely(!init) && rng_is_initialized()) {
+ init = true;
+ lrng_krng_adjust_entropy();
+ }
+
+ return lrng_fast_noise_entropylevel(
+ lrng_krng_fips_entropylevel(krng_entropy), requested_bits);
+}
+
+static u32 lrng_krng_poolsize(void)
+{
+ return lrng_krng_entropylevel(lrng_security_strength());
+}
+
+/*
+ * lrng_krng_get() - Get kernel RNG entropy
+ *
+ * @eb: entropy buffer to store entropy
+ * @requested_bits: requested entropy in bits
+ */
+static void lrng_krng_get(struct entropy_buf *eb, u32 requested_bits,
+ bool __unused)
+{
+ u32 ent_bits = lrng_krng_entropylevel(requested_bits);
+
+ get_random_bytes(eb->e[lrng_ext_es_krng], requested_bits >> 3);
+
+ pr_debug("obtained %u bits of entropy from kernel RNG noise source\n",
+ ent_bits);
+
+ eb->e_bits[lrng_ext_es_krng] = ent_bits;
+}
+
+static void lrng_krng_es_state(unsigned char *buf, size_t buflen)
+{
+ snprintf(buf, buflen,
+ " Available entropy: %u\n"
+ " Entropy Rate per 256 data bits: %u\n",
+ lrng_krng_poolsize(),
+ lrng_krng_entropylevel(256));
+}
+
+struct lrng_es_cb lrng_es_krng = {
+ .name = "KernelRNG",
+ .get_ent = lrng_krng_get,
+ .curr_entropy = lrng_krng_entropylevel,
+ .max_entropy = lrng_krng_poolsize,
+ .state = lrng_krng_es_state,
+ .reset = NULL,
+ .switch_hash = NULL,
+};
From 67d7584f291dc9e3fb5a59645fce2fc8cadf3e38 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Mon, 20 Feb 2023 22:08:23 +0100
Subject: [PATCH 16/25] 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.
Some CPU entropy sources (e.g. POWER DARN, RISC-V) are defined to
deliver data that does not contain full entropy. In this case, the CPU
is sampled for as much data as needed to obtain the required amount of
entropy. In this case, a compression operation using the set message
digest is applied.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 76 ++++-----
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_es_cpu.c | 281 ++++++++++++++++++++++++++++++++
3 files changed, 320 insertions(+), 38 deletions(-)
create mode 100644 drivers/char/lrng/lrng_es_cpu.c
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -496,44 +496,44 @@ config LRNG_IRQ_ENTROPY_RATE
# will provide 256 bits of data without being credited to contain
# 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.
-#
+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.
+
comment "Scheduler Entropy Source"
config LRNG_SCHED
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -22,5 +22,6 @@ obj-$(CONFIG_LRNG_TIMER_COMMON) += lrng
obj-$(CONFIG_LRNG_IRQ) += lrng_es_irq.o
obj-$(CONFIG_LRNG_KERNEL_RNG) += lrng_es_krng.o
obj-$(CONFIG_LRNG_SCHED) += lrng_es_sched.o
+obj-$(CONFIG_LRNG_CPU) += lrng_es_cpu.o
obj-$(CONFIG_LRNG_HEALTH_TESTS) += lrng_health.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_cpu.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG Fast Entropy Source: CPU-based entropy source
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/lrng.h>
+#include <crypto/hash.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <asm/archrandom.h>
+
+#include "lrng_definitions.h"
+#include "lrng_es_aux.h"
+#include "lrng_es_cpu.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 cpu_entropy = LRNG_ARCHRANDOM_TRUST_CPU_STRENGTH;
+#else
+static u32 cpu_entropy = LRNG_ARCHRANDOM_DEFAULT_STRENGTH;
+#endif
+#ifdef CONFIG_LRNG_RUNTIME_ES_CONFIG
+module_param(cpu_entropy, uint, 0644);
+MODULE_PARM_DESC(cpu_entropy, "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)
+ cpu_entropy = LRNG_ARCHRANDOM_TRUST_CPU_STRENGTH;
+ else
+ cpu_entropy = LRNG_ARCHRANDOM_DEFAULT_STRENGTH;
+
+ lrng_force_fully_seeded();
+
+ return 0;
+}
+early_param("random.trust_cpu", lrng_parse_trust_cpu);
+
+static u32 lrng_cpu_entropylevel(u32 requested_bits)
+{
+ return lrng_fast_noise_entropylevel(cpu_entropy, requested_bits);
+}
+
+static u32 lrng_cpu_poolsize(void)
+{
+ return lrng_cpu_entropylevel(lrng_security_strength());
+}
+
+static u32 lrng_get_cpu_data(u8 *outbuf, u32 requested_bits)
+{
+ size_t longs = 0;
+ u32 i, req = requested_bits >> 3;
+
+ /* operate on full blocks */
+ BUILD_BUG_ON(LRNG_DRNG_SECURITY_STRENGTH_BYTES % sizeof(unsigned long));
+ BUILD_BUG_ON(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 < req; i += longs) {
+ longs = arch_get_random_seed_longs(
+ (unsigned long *)(outbuf + i), req - i);
+ if (longs)
+ continue;
+ longs = arch_get_random_longs((unsigned long *)(outbuf + i),
+ req - i);
+ if (!longs) {
+ cpu_entropy = 0;
+ return 0;
+ }
+ }
+
+ return requested_bits;
+}
+
+static u32 lrng_get_cpu_data_compress(u8 *outbuf, u32 requested_bits,
+ u32 data_multiplier)
+{
+ SHASH_DESC_ON_STACK(shash, NULL);
+ const struct lrng_hash_cb *hash_cb;
+ struct lrng_drng *drng = lrng_drng_node_instance();
+ unsigned long flags;
+ u32 ent_bits = 0, i, partial_bits = 0, digestsize, digestsize_bits,
+ full_bits;
+ void *hash;
+
+ read_lock_irqsave(&drng->hash_lock, flags);
+ hash_cb = drng->hash_cb;
+ hash = drng->hash;
+
+ digestsize = hash_cb->hash_digestsize(hash);
+ digestsize_bits = digestsize << 3;
+ /* Cap to maximum entropy that can ever be generated with given hash */
+ lrng_cap_requested(digestsize_bits, requested_bits);
+ full_bits = requested_bits * data_multiplier;
+
+ /* Calculate oversampling for SP800-90C */
+ if (lrng_sp80090c_compliant()) {
+ /* Complete amount of bits to be pulled */
+ full_bits += 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);
+ }
+
+ if (hash_cb->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_cpu_data(outbuf, requested_bits);
+ if (!ent_bits)
+ goto out;
+
+ if (hash_cb->hash_update(shash, outbuf, ent_bits >> 3))
+ goto err;
+ }
+
+ /* Hash partial block, if applicable */
+ ent_bits = lrng_get_cpu_data(outbuf, partial_bits);
+ if (ent_bits &&
+ hash_cb->hash_update(shash, outbuf, ent_bits >> 3))
+ goto err;
+
+ pr_debug("pulled %u bits from CPU RNG entropy source\n", full_bits);
+ ent_bits = requested_bits;
+
+ /* Generate the compressed data to be returned to the caller */
+ if (requested_bits < digestsize_bits) {
+ u8 digest[LRNG_MAX_DIGESTSIZE];
+
+ if (hash_cb->hash_final(shash, digest))
+ goto err;
+
+ /* Truncate output data to requested size */
+ memcpy(outbuf, digest, requested_bits >> 3);
+ memzero_explicit(digest, digestsize);
+ } else {
+ if (hash_cb->hash_final(shash, outbuf))
+ goto err;
+ }
+
+out:
+ hash_cb->hash_desc_zero(shash);
+ read_unlock_irqrestore(&drng->hash_lock, 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_cpu_multiplier(void)
+{
+ static u32 data_multiplier = 0;
+ unsigned long v;
+
+ if (data_multiplier > 0)
+ return data_multiplier;
+
+ if (IS_ENABLED(CONFIG_X86) && !arch_get_random_seed_longs(&v, 1)) {
+ /*
+ * 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 if (IS_ENABLED(CONFIG_RISCV)) {
+ /*
+ * riscv-crypto-spec-scalar-1.0.0-rc6.pdf section 4.2 defines
+ * this requirement.
+ */
+ data_multiplier = 2;
+ } else {
+ /* CPU provides full entropy */
+ data_multiplier = CONFIG_LRNG_CPU_FULL_ENT_MULTIPLIER;
+ }
+ return data_multiplier;
+}
+
+static int
+lrng_cpu_switch_hash(struct lrng_drng *drng, int node,
+ const struct lrng_hash_cb *new_cb, void *new_hash,
+ const struct lrng_hash_cb *old_cb)
+{
+ u32 digestsize, multiplier;
+
+ if (!IS_ENABLED(CONFIG_LRNG_SWITCH))
+ return -EOPNOTSUPP;
+
+ digestsize = lrng_get_digestsize();
+ multiplier = lrng_cpu_multiplier();
+
+ /*
+ * It would be security violation if the new digestsize is smaller than
+ * the set CPU entropy rate.
+ */
+ WARN_ON(multiplier > 1 && digestsize < cpu_entropy);
+ cpu_entropy = min_t(u32, digestsize, cpu_entropy);
+ return 0;
+}
+
+/*
+ * lrng_get_arch() - Get CPU entropy source entropy
+ *
+ * @eb: entropy buffer to store entropy
+ * @requested_bits: requested entropy in bits
+ */
+static void lrng_cpu_get(struct entropy_buf *eb, u32 requested_bits,
+ bool __unused)
+{
+ u32 ent_bits, data_multiplier = lrng_cpu_multiplier();
+
+ if (data_multiplier <= 1) {
+ ent_bits = lrng_get_cpu_data(eb->e[lrng_ext_es_cpu],
+ requested_bits);
+ } else {
+ ent_bits = lrng_get_cpu_data_compress(eb->e[lrng_ext_es_cpu],
+ requested_bits,
+ data_multiplier);
+ }
+
+ ent_bits = lrng_cpu_entropylevel(ent_bits);
+ pr_debug("obtained %u bits of entropy from CPU RNG entropy source\n",
+ ent_bits);
+ eb->e_bits[lrng_ext_es_cpu] = ent_bits;
+}
+
+static void lrng_cpu_es_state(unsigned char *buf, size_t buflen)
+{
+ const struct lrng_drng *lrng_drng_init = lrng_drng_init_instance();
+ u32 data_multiplier = lrng_cpu_multiplier();
+
+ /* Assume the lrng_drng_init lock is taken by caller */
+ snprintf(buf, buflen,
+ " Hash for compressing data: %s\n"
+ " Available entropy: %u\n"
+ " Data multiplier: %u\n",
+ (data_multiplier <= 1) ?
+ "N/A" : lrng_drng_init->hash_cb->hash_name(),
+ lrng_cpu_poolsize(),
+ data_multiplier);
+}
+
+struct lrng_es_cb lrng_es_cpu = {
+ .name = "CPU",
+ .get_ent = lrng_cpu_get,
+ .curr_entropy = lrng_cpu_entropylevel,
+ .max_entropy = lrng_cpu_poolsize,
+ .state = lrng_cpu_es_state,
+ .reset = NULL,
+ .switch_hash = lrng_cpu_switch_hash,
+};
From a9b4e46d8253ca9e63fcf1fed126c407a1e4f73c Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Sun, 27 Aug 2023 17:11:37 +0200
Subject: [PATCH 17/25] 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.
The default entropy rate is set to 16 bits of entropy per 256 bits of
data. In FIPS mode, the ratio is set to 256 bits of entropy per 256 bits
of data considering that the Jitter RNG passed the NIST ENT validation
to claim full entropy.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 172 +++++++--------
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_es_jent.c | 358 +++++++++++++++++++++++++++++++
3 files changed, 445 insertions(+), 86 deletions(-)
create mode 100644 drivers/char/lrng/lrng_es_jent.c
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -410,92 +410,92 @@ 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 LRNG may use the Jitter RNG as entropy 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.
-#
-#choice
-# prompt "Jitter RNG Async Block Number"
-# default LRNG_JENT_ENTROPY_BLOCKS_NO_128
-# depends on LRNG_JENT
-# help
-# Select the number of Jitter RNG entropy blocks the asynchronous
-# collection operation will fill. A caller for Jitter RNG entropy
-# will be given data from the pre-filled blocks if available to
-# prevent the Jitter RNG from utilizing the CPU too much in a
-# possible hot code path.
-#
-# The number specifies the number of 256/384 bit blocks that will
-# be held in memory and asynchronously filled with Jitter RNG data.
-#
-# The asynchronous entropy collection can also be disabled at
-# kernel startup time when setting the command line option of
-# lrng_es_jent.jent_async_enabled=0. Also, setting this option at
-# runtime is allowed via the corresponding SysFS interface. This
-# option is only available with the options SysFS and
-# CONFIG_LRNG_RUNTIME_ES_CONFIG enabled.
-#
-# config LRNG_JENT_ENTROPY_BLOCKS_DISABLED
-# bool "Async collection disabled"
-#
-# # Any block number is allowed, provided it is a power of 2 and
-# # equal or larger than 4 (4 is due to the division in
-# # lrng_jent_async_get when deciding to wake up the monitor).
-# config LRNG_JENT_ENTROPY_BLOCKS_NO_32
-# bool "32 blocks"
-#
-# config LRNG_JENT_ENTROPY_BLOCKS_NO_64
-# bool "64 blocks"
-#
-# config LRNG_JENT_ENTROPY_BLOCKS_NO_128
-# bool "128 blocks (default)"
-#
-# config LRNG_JENT_ENTROPY_BLOCKS_NO_256
-# bool "256 blocks"
-#
-# config LRNG_JENT_ENTROPY_BLOCKS_NO_512
-# bool "512 blocks"
-#
-# config LRNG_JENT_ENTROPY_BLOCKS_NO_1024
-# bool "1024 blocks"
-#
-#endchoice
-#
-#config LRNG_JENT_ENTROPY_BLOCKS
-# int
-# default 0 if LRNG_JENT_ENTROPY_BLOCKS_DISABLED
-# default 32 if LRNG_JENT_ENTROPY_BLOCKS_NO_32
-# default 64 if LRNG_JENT_ENTROPY_BLOCKS_NO_64
-# default 128 if LRNG_JENT_ENTROPY_BLOCKS_NO_128
-# default 256 if LRNG_JENT_ENTROPY_BLOCKS_NO_256
-# default 512 if LRNG_JENT_ENTROPY_BLOCKS_NO_512
-# default 1024 if LRNG_JENT_ENTROPY_BLOCKS_NO_1024
-#
-# 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 "Jitter RNG Entropy Source"
+
+config LRNG_JENT
+ bool "Enable Jitter RNG as LRNG Seed Source"
+ depends on CRYPTO
+ select CRYPTO_JITTERENTROPY
+ help
+ The LRNG may use the Jitter RNG as entropy 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.
+
+choice
+ prompt "Jitter RNG Async Block Number"
+ default LRNG_JENT_ENTROPY_BLOCKS_NO_128
+ depends on LRNG_JENT
+ help
+ Select the number of Jitter RNG entropy blocks the asynchronous
+ collection operation will fill. A caller for Jitter RNG entropy
+ will be given data from the pre-filled blocks if available to
+ prevent the Jitter RNG from utilizing the CPU too much in a
+ possible hot code path.
+
+ The number specifies the number of 256/384 bit blocks that will
+ be held in memory and asynchronously filled with Jitter RNG data.
+
+ The asynchronous entropy collection can also be disabled at
+ kernel startup time when setting the command line option of
+ lrng_es_jent.jent_async_enabled=0. Also, setting this option at
+ runtime is allowed via the corresponding SysFS interface. This
+ option is only available with the options SysFS and
+ CONFIG_LRNG_RUNTIME_ES_CONFIG enabled.
+
+ config LRNG_JENT_ENTROPY_BLOCKS_DISABLED
+ bool "Async collection disabled"
+
+ # Any block number is allowed, provided it is a power of 2 and
+ # equal or larger than 4 (4 is due to the division in
+ # lrng_jent_async_get when deciding to wake up the monitor).
+ config LRNG_JENT_ENTROPY_BLOCKS_NO_32
+ bool "32 blocks"
+
+ config LRNG_JENT_ENTROPY_BLOCKS_NO_64
+ bool "64 blocks"
+
+ config LRNG_JENT_ENTROPY_BLOCKS_NO_128
+ bool "128 blocks (default)"
+
+ config LRNG_JENT_ENTROPY_BLOCKS_NO_256
+ bool "256 blocks"
+
+ config LRNG_JENT_ENTROPY_BLOCKS_NO_512
+ bool "512 blocks"
+
+ config LRNG_JENT_ENTROPY_BLOCKS_NO_1024
+ bool "1024 blocks"
+
+endchoice
+
+config LRNG_JENT_ENTROPY_BLOCKS
+ int
+ default 0 if LRNG_JENT_ENTROPY_BLOCKS_DISABLED
+ default 32 if LRNG_JENT_ENTROPY_BLOCKS_NO_32
+ default 64 if LRNG_JENT_ENTROPY_BLOCKS_NO_64
+ default 128 if LRNG_JENT_ENTROPY_BLOCKS_NO_128
+ default 256 if LRNG_JENT_ENTROPY_BLOCKS_NO_256
+ default 512 if LRNG_JENT_ENTROPY_BLOCKS_NO_512
+ default 1024 if LRNG_JENT_ENTROPY_BLOCKS_NO_1024
+
+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
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -23,5 +23,6 @@ obj-$(CONFIG_LRNG_IRQ) += lrng_es_irq.
obj-$(CONFIG_LRNG_KERNEL_RNG) += lrng_es_krng.o
obj-$(CONFIG_LRNG_SCHED) += lrng_es_sched.o
obj-$(CONFIG_LRNG_CPU) += lrng_es_cpu.o
+obj-$(CONFIG_LRNG_JENT) += lrng_es_jent.o
obj-$(CONFIG_LRNG_HEALTH_TESTS) += lrng_health.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_es_jent.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG Fast Entropy Source: Jitter RNG
+ *
+ * Copyright (C) 2022 - 2023, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <crypto/rng.h>
+#include <linux/fips.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include "lrng_definitions.h"
+#include "lrng_es_aux.h"
+#include "lrng_es_jent.h"
+#include "lrng_es_mgr.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 jent_entropy = CONFIG_LRNG_JENT_ENTROPY_RATE;
+#ifdef CONFIG_LRNG_RUNTIME_ES_CONFIG
+module_param(jent_entropy, uint, 0644);
+MODULE_PARM_DESC(jent_entropy,
+ "Entropy in bits of 256 data bits from Jitter RNG noise source");
+#endif
+
+static bool lrng_jent_initialized = false;
+static struct crypto_rng *jent;
+
+#if (CONFIG_LRNG_JENT_ENTROPY_BLOCKS != 0)
+
+/* Entropy buffer filled by Jitter RNG thread - must be power of 2 */
+#define LRNG_JENT_ENTROPY_BLOCKS_MASK (CONFIG_LRNG_JENT_ENTROPY_BLOCKS - 1)
+
+struct jent_entropy_es {
+ uint8_t e[LRNG_DRNG_INIT_SEED_SIZE_BYTES];
+ uint32_t e_bits;
+};
+
+/* Buffer that is filled with Jitter RNG data by a thread. */
+static struct jent_entropy_es
+ lrng_jent_async[CONFIG_LRNG_JENT_ENTROPY_BLOCKS] __aligned(sizeof(u64));
+
+/* State of each Jitter RNG buffer entry to ensure atomic access. */
+enum lrng_jent_async_state {
+ buffer_empty,
+ buffer_filling,
+ buffer_filled,
+ buffer_reading,
+};
+static atomic_t lrng_jent_async_set[CONFIG_LRNG_JENT_ENTROPY_BLOCKS];
+
+/* Jitter RNG buffer work handler. */
+static struct work_struct lrng_jent_async_work;
+
+/* Is the asynchronous operation enabled? */
+static bool lrng_es_jent_async_enabled = true;
+
+#else /* CONFIG_LRNG_JENT_ENTROPY_BLOCKS */
+
+/* The asynchronous operation is disabled by compile time option. */
+static bool lrng_es_jent_async_enabled = false;
+
+#endif /* CONFIG_LRNG_JENT_ENTROPY_BLOCKS */
+
+static u32 lrng_jent_entropylevel(u32 requested_bits)
+{
+ return lrng_fast_noise_entropylevel(lrng_jent_initialized ?
+ jent_entropy : 0, requested_bits);
+}
+
+static u32 lrng_jent_poolsize(void)
+{
+ return lrng_jent_entropylevel(lrng_security_strength());
+}
+
+static void __lrng_jent_get(u8 *e, u32 *e_bits, u32 requested_bits)
+{
+ int ret;
+ u32 ent_bits = lrng_jent_entropylevel(requested_bits);
+ unsigned long flags;
+ static DEFINE_SPINLOCK(lrng_jent_lock);
+
+ if (!lrng_jent_initialized)
+ goto err;
+
+ spin_lock_irqsave(&lrng_jent_lock, flags);
+ ret = crypto_rng_get_bytes(jent, e, requested_bits >> 3);
+ spin_unlock_irqrestore(&lrng_jent_lock, flags);
+
+ if (ret) {
+ pr_debug("Jitter RNG failed with %d\n", ret);
+ goto err;
+ }
+
+ pr_debug("obtained %u bits of entropy from Jitter RNG noise source\n",
+ ent_bits);
+
+ *e_bits = ent_bits;
+ return;
+
+err:
+ *e_bits = 0;
+}
+
+/*
+ * lrng_get_jent() - Get Jitter RNG entropy
+ *
+ * @eb: entropy buffer to store entropy
+ * @requested_bits: requested entropy in bits
+ */
+static void lrng_jent_get(struct entropy_buf *eb, u32 requested_bits,
+ bool __unused)
+{
+ __lrng_jent_get(eb->e[lrng_ext_es_jitter],
+ &eb->e_bits[lrng_ext_es_jitter], requested_bits);
+}
+
+#if (CONFIG_LRNG_JENT_ENTROPY_BLOCKS != 0)
+
+/* Fill the Jitter RNG buffer with random data. */
+static void lrng_jent_async_monitor(struct work_struct *__unused)
+{
+ unsigned int i, requested_bits = lrng_get_seed_entropy_osr(true);
+
+ pr_debug("Jitter RNG block filling started\n");
+
+ for (i = 0; i < CONFIG_LRNG_JENT_ENTROPY_BLOCKS; i++) {
+ /* Ensure atomic access to the Jitter RNG buffer slot. */
+ if (atomic_cmpxchg(&lrng_jent_async_set[i],
+ buffer_empty, buffer_filling) !=
+ buffer_empty)
+ continue;
+
+ /*
+ * Always gather entropy data including
+ * potential oversampling factor.
+ */
+ __lrng_jent_get(lrng_jent_async[i].e,
+ &lrng_jent_async[i].e_bits, requested_bits);
+
+ atomic_set(&lrng_jent_async_set[i], buffer_filled);
+
+ pr_debug("Jitter RNG ES monitor: filled slot %u with %u bits of entropy\n",
+ i, requested_bits);
+
+ schedule();
+ }
+
+ pr_debug("Jitter RNG block filling completed\n");
+}
+
+static void lrng_jent_async_monitor_schedule(void)
+{
+ if (lrng_es_jent_async_enabled)
+ schedule_work(&lrng_jent_async_work);
+}
+
+static void lrng_jent_async_fini(void)
+{
+ /* Reset state */
+ memzero_explicit(lrng_jent_async, sizeof(lrng_jent_async));
+}
+
+/* Get Jitter RNG data from the buffer */
+static void lrng_jent_async_get(struct entropy_buf *eb, uint32_t requested_bits,
+ bool __unused)
+{
+ static atomic_t idx = ATOMIC_INIT(-1);
+ unsigned int slot;
+
+ (void)requested_bits;
+
+ if (!lrng_jent_initialized) {
+ eb->e_bits[lrng_ext_es_jitter] = 0;
+ return;
+ }
+
+ /* CONFIG_LRNG_JENT_ENTROPY_BLOCKS must be a power of 2 */
+ BUILD_BUG_ON((CONFIG_LRNG_JENT_ENTROPY_BLOCKS &
+ LRNG_JENT_ENTROPY_BLOCKS_MASK) != 0);
+
+ slot = ((unsigned int)atomic_inc_return(&idx)) &
+ LRNG_JENT_ENTROPY_BLOCKS_MASK;
+
+ /* Ensure atomic access to the Jitter RNG buffer slot. */
+ if (atomic_cmpxchg(&lrng_jent_async_set[slot],
+ buffer_filled, buffer_reading) != buffer_filled) {
+ pr_debug("Jitter RNG ES monitor: buffer slot %u exhausted\n",
+ slot);
+ lrng_jent_get(eb, requested_bits, __unused);
+ lrng_jent_async_monitor_schedule();
+ return;
+ }
+
+ pr_debug("Jitter RNG ES monitor: used slot %u\n", slot);
+ memcpy(eb->e[lrng_ext_es_jitter], lrng_jent_async[slot].e,
+ LRNG_DRNG_INIT_SEED_SIZE_BYTES);
+ eb->e_bits[lrng_ext_es_jitter] = lrng_jent_async[slot].e_bits;
+
+ pr_debug("obtained %u bits of entropy from Jitter RNG noise source\n",
+ eb->e_bits[lrng_ext_es_jitter]);
+
+ memzero_explicit(&lrng_jent_async[slot],
+ sizeof(struct jent_entropy_es));
+
+ atomic_set(&lrng_jent_async_set[slot], buffer_empty);
+
+ /* Ensure division in the following check works */
+ BUILD_BUG_ON(CONFIG_LRNG_JENT_ENTROPY_BLOCKS < 4);
+ if (!(slot % (CONFIG_LRNG_JENT_ENTROPY_BLOCKS / 4)) && slot)
+ lrng_jent_async_monitor_schedule();
+}
+
+static void lrng_jent_get_check(struct entropy_buf *eb,
+ uint32_t requested_bits, bool __unused)
+{
+ if (lrng_es_jent_async_enabled &&
+ (requested_bits == lrng_get_seed_entropy_osr(true))) {
+ lrng_jent_async_get(eb, requested_bits, __unused);
+ } else {
+ lrng_jent_get(eb, requested_bits, __unused);
+ }
+}
+
+static void lrng_jent_async_init(void)
+{
+ unsigned int i;
+
+ if (!lrng_es_jent_async_enabled)
+ return;
+
+ for (i = 0; i < CONFIG_LRNG_JENT_ENTROPY_BLOCKS; i++)
+ atomic_set(&lrng_jent_async_set[i], buffer_empty);
+}
+
+static void lrng_jent_async_init_complete(void)
+{
+ lrng_jent_async_init();
+ INIT_WORK(&lrng_jent_async_work, lrng_jent_async_monitor);
+}
+
+#if (defined(CONFIG_SYSFS) && defined(CONFIG_LRNG_RUNTIME_ES_CONFIG))
+/* Initialize or deinitialize the Jitter RNG async collection */
+static int lrng_jent_async_sysfs_set(const char *val,
+ const struct kernel_param *kp)
+{
+ static const char val_dflt[] = "1";
+ int ret;
+ bool setting;
+
+ if (!val)
+ val = val_dflt;
+
+ ret = kstrtobool(val, &setting);
+ if (ret)
+ return ret;
+
+ if (setting) {
+ if (!lrng_es_jent_async_enabled) {
+ lrng_es_jent_async_enabled = 1;
+ lrng_jent_async_init();
+ pr_devel("Jitter RNG async data collection enabled\n");
+ lrng_jent_async_monitor_schedule();
+ }
+ } else {
+ if (lrng_es_jent_async_enabled) {
+ lrng_es_jent_async_enabled = 0;
+ lrng_jent_async_fini();
+ pr_devel("Jitter RNG async data collection disabled\n");
+ }
+ }
+
+ return 0;
+}
+
+static const struct kernel_param_ops lrng_jent_async_sysfs = {
+ .set = lrng_jent_async_sysfs_set,
+ .get = param_get_bool,
+};
+module_param_cb(jent_async_enabled, &lrng_jent_async_sysfs,
+ &lrng_es_jent_async_enabled, 0644);
+MODULE_PARM_DESC(lrng_es_jent_async_enabled,
+ "Enable Jitter RNG entropy buffer asynchronous collection");
+#endif /* CONFIG_SYSFS && CONFIG_LRNG_RUNTIME_ES_CONFIG */
+
+#else /* CONFIG_LRNG_JENT_ENTROPY_BLOCKS */
+
+static void lrng_jent_get_check(struct entropy_buf *eb,
+ uint32_t requested_bits, bool __unused)
+{
+ lrng_jent_get(eb, requested_bits, __unused);
+}
+
+static inline void __init lrng_jent_async_init_complete(void) { }
+
+#endif /* CONFIG_LRNG_JENT_ENTROPY_BLOCKS */
+
+static void lrng_jent_es_state(unsigned char *buf, size_t buflen)
+{
+ snprintf(buf, buflen,
+ " Available entropy: %u\n"
+ " Enabled: %s\n"
+ " Jitter RNG async collection %s\n",
+ lrng_jent_poolsize(),
+ lrng_jent_initialized ? "true" : "false",
+ lrng_es_jent_async_enabled ? "true" : "false");
+}
+
+static int __init lrng_jent_initialize(void)
+{
+ jent = crypto_alloc_rng("jitterentropy_rng", 0, 0);
+ if (IS_ERR(jent)) {
+ pr_err("Cannot allocate Jitter RNG\n");
+ return PTR_ERR(jent);
+ }
+
+ lrng_jent_async_init_complete();
+
+ lrng_jent_initialized = true;
+ pr_debug("Jitter RNG working on current system\n");
+
+ /*
+ * In FIPS mode, the Jitter RNG is defined to have full of entropy
+ * unless a different value has been specified at the command line
+ * (i.e. the user overrides the default), and the default value is
+ * larger than zero (if it is zero, it is assumed that an RBG2(P) or
+ * RBG2(NP) construction is attempted that intends to exclude the
+ * Jitter RNG).
+ */
+ if (fips_enabled && CONFIG_LRNG_JENT_ENTROPY_RATE > 0 &&
+ jent_entropy == CONFIG_LRNG_JENT_ENTROPY_RATE)
+ jent_entropy = LRNG_DRNG_SECURITY_STRENGTH_BITS;
+
+ if (jent_entropy)
+ lrng_force_fully_seeded();
+
+ return 0;
+}
+device_initcall(lrng_jent_initialize);
+
+struct lrng_es_cb lrng_es_jent = {
+ .name = "JitterRNG",
+ .get_ent = lrng_jent_get_check,
+ .curr_entropy = lrng_jent_entropylevel,
+ .max_entropy = lrng_jent_poolsize,
+ .state = lrng_jent_es_state,
+ .reset = NULL,
+ .switch_hash = NULL,
+};
From 718c21ecd7a9ac35fbdf28d2dcd2310634c669ef Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Sun, 15 May 2022 17:56:56 +0200
Subject: [PATCH 18/25] LRNG - add option to enable runtime entropy rate
configuration
The entropy rate for the different entropy sources is configured at
compile time. Enabling the option CONFIG_LRNG_RUNTIME_ES_CONFIG allows
the entropy sources' entropy rate to be adjusted at runtime or boot
time. The different options are:
- IRQ ES: lrng_es_irq.irq_entropy
- Scheduler ES: lrng_es_sched.sched_entropy
- CPU ES: lrng_es_cpu.cpu_entropy
- Kernel ES: lrng_es_krng.krng_entropy
- Jitter RNG ES: lrng_es_jent.jent_entropy
The values to be specified there must apply the rationale given for the
different CONFIG_LRNG_*_ENTROPY_RATE kernel configuration options.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -124,17 +124,17 @@ endmenu # "Specific DRNG seeding strateg
menu "Entropy Source Configuration"
-# 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.
-#
-# Each entropy source allows its entropy rate changed with
-# a kernel command line option. When not providing any
-# option, the default specified during kernel compilation
-# is applied.
+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.
+
+ Each entropy source allows its entropy rate changed with
+ a kernel command line option. When not providing any
+ option, the default specified during kernel compilation
+ is applied.
comment "Common Timer-based Entropy Source Configuration"
From 78cb1213ad35895b6d0d1a1ff49224f0c123e86c Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Sun, 18 Dec 2022 21:22:36 +0100
Subject: [PATCH 19/25] 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.
The raw entropy collection is provided for the internal entropy sources
under full control of the LRNG: the IRQ ES and the scheduler-based ES.
The test interfaces are only enabled for the given ES if the ES is
enabled itself.
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 IRQ data:
* high-resolution time stamp obtained for an IRQ event
* Jiffies
* IRQ number
* return instruction pointer
* interrupt register state
* array logic batching the high-resolution time stamp
* a performance monitor of the IRQ ES
The sampling of the following scheduler-based ES data is possible:
* high-resolution time stamp obtained for a context switch event
* the PID of the process scheduled to
* the process' start time value
* the process' context switch numbers value
* a performance monitor of the scheduler-based ES
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.
If a test interface is not compiled, its code is a noop which has no
impact on the performance.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 534 +++++++++---------
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_testing.c | 901 +++++++++++++++++++++++++++++++
3 files changed, 1169 insertions(+), 267 deletions(-)
create mode 100644 drivers/char/lrng/lrng_testing.c
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -718,273 +718,273 @@ choice
select LRNG_DRNG_KCAPI
endchoice
-# 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_TESTING
-# bool
-#
-# config LRNG_TESTING_RECORDING
-# bool
-#
-# comment "Interrupt Entropy Source Test Interfaces"
-#
-# config LRNG_RAW_HIRES_ENTROPY
-# bool "Interface to obtain raw unprocessed IRQ noise source data"
-# default y
-# depends on LRNG_IRQ
-# select LRNG_TESTING
-# select LRNG_TESTING_RECORDING
-# 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 "Entropy test interface to Jiffies of IRQ noise source"
-# depends on LRNG_IRQ
-# select LRNG_TESTING
-# select LRNG_TESTING_RECORDING
-# 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 "Entropy test interface to IRQ number noise source"
-# depends on LRNG_IRQ
-# select LRNG_TESTING
-# select LRNG_TESTING_RECORDING
-# help
-# The test interface allows a privileged process to capture
-# the raw unconditioned interrupt number 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_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_RETIP_ENTROPY
-# bool "Entropy test interface to RETIP value of IRQ noise source"
-# depends on LRNG_IRQ
-# select LRNG_TESTING
-# select LRNG_TESTING_RECORDING
-# 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.
-# 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 "Entropy test interface to IRQ register value noise source"
-# depends on LRNG_IRQ
-# select LRNG_TESTING
-# select LRNG_TESTING_RECORDING
-# 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 "Test interface to LRNG raw entropy IRQ storage array"
-# depends on LRNG_IRQ
-# select LRNG_TESTING
-# select LRNG_TESTING_RECORDING
-# 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 "LRNG interrupt entropy source performance monitor"
-# depends on LRNG_IRQ
-# select LRNG_TESTING
-# select LRNG_TESTING_RECORDING
-# 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.
-#
-# comment "Scheduler Entropy Source Test Interfaces"
-#
-# config LRNG_RAW_SCHED_HIRES_ENTROPY
-# bool "Interface to obtain raw unprocessed scheduler noise source data"
-# depends on LRNG_SCHED
-# select LRNG_TESTING
-# select LRNG_TESTING_RECORDING
-# 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 the Scheduler-based noise source
-# for statistical analysis. Extracted noise data is not used to
-# seed the LRNG.
-#
-# The raw noise data can be obtained using the lrng_raw_sched_hires
-# debugfs file. Using the option
-# lrng_testing.boot_raw_sched_hires_test=1 the raw noise of the
-# first 1000 entropy events since boot can be sampled.
-#
-# config LRNG_RAW_SCHED_PID_ENTROPY
-# bool "Entropy test interface to PID value"
-# depends on LRNG_SCHED
-# select LRNG_TESTING
-# select LRNG_TESTING_RECORDING
-# help
-# The test interface allows a privileged process to capture
-# the raw unconditioned PID 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_sched_pid debugfs file. Using the option
-# lrng_testing.boot_raw_sched_pid_test=1
-# the raw noise of the first 1000 entropy events since boot
-# can be sampled.
-#
-# config LRNG_RAW_SCHED_START_TIME_ENTROPY
-# bool "Entropy test interface to task start time value"
-# depends on LRNG_SCHED
-# select LRNG_TESTING
-# select LRNG_TESTING_RECORDING
-# help
-# The test interface allows a privileged process to capture
-# the raw unconditioned task start time 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_sched_starttime debugfs file. Using the option
-# lrng_testing.boot_raw_sched_starttime_test=1
-# the raw noise of the first 1000 entropy events since boot
-# can be sampled.
-#
-#
-# config LRNG_RAW_SCHED_NVCSW_ENTROPY
-# bool "Entropy test interface to task context switch numbers"
-# depends on LRNG_SCHED
-# select LRNG_TESTING
-# select LRNG_TESTING_RECORDING
-# help
-# The test interface allows a privileged process to capture
-# the raw unconditioned task numbers of context switches that
-# are 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_sched_nvcsw debugfs file. Using the option
-# lrng_testing.boot_raw_sched_nvcsw_test=1
-# the raw noise of the first 1000 entropy events since boot
-# can be sampled.
-#
-# config LRNG_SCHED_PERF
-# bool "LRNG scheduler entropy source performance monitor"
-# depends on LRNG_SCHED
-# select LRNG_TESTING
-# select LRNG_TESTING_RECORDING
-# help
-# With this option, the performance monitor of the LRNG
-# scheduler event handling code is enabled. The file provides
-# the execution time of the interrupt handler in cycles.
-#
-# The scheduler performance data can be obtained using
-# the lrng_sched_perf debugfs file. Using the option
-# lrng_testing.boot_sched_perf=1 the performance data of
-# the first 1000 entropy events since boot can be sampled.
-#
-# comment "Auxiliary Test Interfaces"
-#
-# config LRNG_ACVT_HASH
-# bool "Enable LRNG ACVT Hash interface"
-# select LRNG_TESTING
-# 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_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_RUNTIME_FORCE_SEEDING_DISABLE
-# bool "Enable runtime configuration of force seeding"
-# help
-# When enabling this option, the LRNG provides an interface
-# allowing the disabling of the force seeding when the DRNG
-# is not fully seeded but entropy is available.
-#
-# 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.
-#
-# endif #LRNG_TESTING_MENU
+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_TESTING
+ bool
+
+config LRNG_TESTING_RECORDING
+ bool
+
+comment "Interrupt Entropy Source Test Interfaces"
+
+config LRNG_RAW_HIRES_ENTROPY
+ bool "Interface to obtain raw unprocessed IRQ noise source data"
+ default y
+ depends on LRNG_IRQ
+ select LRNG_TESTING
+ select LRNG_TESTING_RECORDING
+ 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 "Entropy test interface to Jiffies of IRQ noise source"
+ depends on LRNG_IRQ
+ select LRNG_TESTING
+ select LRNG_TESTING_RECORDING
+ 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 "Entropy test interface to IRQ number noise source"
+ depends on LRNG_IRQ
+ select LRNG_TESTING
+ select LRNG_TESTING_RECORDING
+ help
+ The test interface allows a privileged process to capture
+ the raw unconditioned interrupt number 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_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_RETIP_ENTROPY
+ bool "Entropy test interface to RETIP value of IRQ noise source"
+ depends on LRNG_IRQ
+ select LRNG_TESTING
+ select LRNG_TESTING_RECORDING
+ 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.
+ 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 "Entropy test interface to IRQ register value noise source"
+ depends on LRNG_IRQ
+ select LRNG_TESTING
+ select LRNG_TESTING_RECORDING
+ 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 "Test interface to LRNG raw entropy IRQ storage array"
+ depends on LRNG_IRQ
+ select LRNG_TESTING
+ select LRNG_TESTING_RECORDING
+ 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 "LRNG interrupt entropy source performance monitor"
+ depends on LRNG_IRQ
+ select LRNG_TESTING
+ select LRNG_TESTING_RECORDING
+ 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.
+
+comment "Scheduler Entropy Source Test Interfaces"
+
+config LRNG_RAW_SCHED_HIRES_ENTROPY
+ bool "Interface to obtain raw unprocessed scheduler noise source data"
+ depends on LRNG_SCHED
+ select LRNG_TESTING
+ select LRNG_TESTING_RECORDING
+ 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 the Scheduler-based noise source
+ for statistical analysis. Extracted noise data is not used to
+ seed the LRNG.
+
+ The raw noise data can be obtained using the lrng_raw_sched_hires
+ debugfs file. Using the option
+ lrng_testing.boot_raw_sched_hires_test=1 the raw noise of the
+ first 1000 entropy events since boot can be sampled.
+
+config LRNG_RAW_SCHED_PID_ENTROPY
+ bool "Entropy test interface to PID value"
+ depends on LRNG_SCHED
+ select LRNG_TESTING
+ select LRNG_TESTING_RECORDING
+ help
+ The test interface allows a privileged process to capture
+ the raw unconditioned PID 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_sched_pid debugfs file. Using the option
+ lrng_testing.boot_raw_sched_pid_test=1
+ the raw noise of the first 1000 entropy events since boot
+ can be sampled.
+
+config LRNG_RAW_SCHED_START_TIME_ENTROPY
+ bool "Entropy test interface to task start time value"
+ depends on LRNG_SCHED
+ select LRNG_TESTING
+ select LRNG_TESTING_RECORDING
+ help
+ The test interface allows a privileged process to capture
+ the raw unconditioned task start time 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_sched_starttime debugfs file. Using the option
+ lrng_testing.boot_raw_sched_starttime_test=1
+ the raw noise of the first 1000 entropy events since boot
+ can be sampled.
+
+
+config LRNG_RAW_SCHED_NVCSW_ENTROPY
+ bool "Entropy test interface to task context switch numbers"
+ depends on LRNG_SCHED
+ select LRNG_TESTING
+ select LRNG_TESTING_RECORDING
+ help
+ The test interface allows a privileged process to capture
+ the raw unconditioned task numbers of context switches that
+ are 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_sched_nvcsw debugfs file. Using the option
+ lrng_testing.boot_raw_sched_nvcsw_test=1
+ the raw noise of the first 1000 entropy events since boot
+ can be sampled.
+
+config LRNG_SCHED_PERF
+ bool "LRNG scheduler entropy source performance monitor"
+ depends on LRNG_SCHED
+ select LRNG_TESTING
+ select LRNG_TESTING_RECORDING
+ help
+ With this option, the performance monitor of the LRNG
+ scheduler event handling code is enabled. The file provides
+ the execution time of the interrupt handler in cycles.
+
+ The scheduler performance data can be obtained using
+ the lrng_sched_perf debugfs file. Using the option
+ lrng_testing.boot_sched_perf=1 the performance data of
+ the first 1000 entropy events since boot can be sampled.
+
+comment "Auxiliary Test Interfaces"
+
+config LRNG_ACVT_HASH
+ bool "Enable LRNG ACVT Hash interface"
+ select LRNG_TESTING
+ 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_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_RUNTIME_FORCE_SEEDING_DISABLE
+ bool "Enable runtime configuration of force seeding"
+ help
+ When enabling this option, the LRNG provides an interface
+ allowing the disabling of the force seeding when the DRNG
+ is not fully seeded but entropy is available.
+
+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.
+
+endif #LRNG_TESTING_MENU
#
# config LRNG_SELFTEST
# bool "Enable power-on and on-demand self-tests"
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -26,3 +26,4 @@ obj-$(CONFIG_LRNG_CPU) += lrng_es_cpu.
obj-$(CONFIG_LRNG_JENT) += lrng_es_jent.o
obj-$(CONFIG_LRNG_HEALTH_TESTS) += lrng_health.o
+obj-$(CONFIG_LRNG_TESTING) += lrng_testing.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_testing.c
@@ -0,0 +1,901 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG testing interfaces to obtain raw entropy
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/lrng.h>
+#include <linux/atomic.h>
+#include <linux/bug.h>
+#include <linux/debugfs.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_definitions.h"
+#include "lrng_drng_chacha20.h"
+#include "lrng_sha.h"
+#include "lrng_testing.h"
+
+#if defined(CONFIG_LRNG_RAW_SCHED_HIRES_ENTROPY) || \
+ defined(CONFIG_LRNG_RAW_SCHED_PID_ENTROPY) || \
+ defined(CONFIG_LRNG_RAW_SCHED_START_TIME_ENTROPY) || \
+ defined(CONFIG_LRNG_RAW_SCHED_NVCSW_ENTROPY) || \
+ defined(CONFIG_LRNG_SCHED_PERF)
+#define LRNG_TESTING_USE_BUSYLOOP
+#endif
+
+#ifdef CONFIG_LRNG_TESTING_RECORDING
+
+#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;
+ atomic_t 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 void lrng_testing_reset(struct lrng_testing *data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->lock, flags);
+ data->rb_reader = 0;
+ atomic_set(&data->rb_writer, 0);
+ spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static 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 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 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 (((u32)atomic_read(&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 (atomic_read(&data->rb_writer) == 1)
+ pr_warn("One time data collection test enabled\n");
+ }
+
+ data->lrng_testing_rb[((u32)atomic_read(&data->rb_writer)) &
+ LRNG_TESTING_RINGBUFFER_MASK] = value;
+ atomic_inc(&data->rb_writer);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+#ifndef LRNG_TESTING_USE_BUSYLOOP
+ if (wq_has_sleeper(&data->read_wait))
+ wake_up_interruptible(&data->read_wait);
+#endif
+
+ return true;
+}
+
+static bool lrng_testing_have_data(struct lrng_testing *data)
+{
+ return ((((u32)atomic_read(&data->rb_writer)) &
+ LRNG_TESTING_RINGBUFFER_MASK) !=
+ (data->rb_reader & LRNG_TESTING_RINGBUFFER_MASK));
+}
+
+static 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) {
+ u32 writer = (u32)atomic_read(&data->rb_writer);
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ /* We have no data or reached the writer. */
+ if (!writer || (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;
+ }
+
+#ifdef LRNG_TESTING_USE_BUSYLOOP
+ while (!lrng_testing_have_data(data))
+ ;
+#else
+ wait_event_interruptible(data->read_wait,
+ lrng_testing_have_data(data));
+#endif
+ 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;
+}
+
+#endif /* CONFIG_LRNG_TESTING_RECORDING */
+
+/************* Raw High-Resolution IRQ 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 IRQ entropy events");
+
+static struct lrng_testing lrng_raw_hires = {
+ .rb_reader = 0,
+ .rb_writer = ATOMIC_INIT(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 = ATOMIC_INIT(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 = ATOMIC_INIT(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 _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 = ATOMIC_INIT(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 = ATOMIC_INIT(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 = ATOMIC_INIT(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 interrupt entropy source performance data");
+
+static struct lrng_testing lrng_irq_perf = {
+ .rb_reader = 0,
+ .rb_writer = ATOMIC_INIT(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 */
+
+/****** Raw High-Resolution Scheduler-based Timer Entropy Data Handling *******/
+
+#ifdef CONFIG_LRNG_RAW_SCHED_HIRES_ENTROPY
+
+static u32 boot_raw_sched_hires_test = 0;
+module_param(boot_raw_sched_hires_test, uint, 0644);
+MODULE_PARM_DESC(boot_raw_sched_hires_test, "Enable gathering boot time high resolution timer entropy of the first Scheduler-based entropy events");
+
+static struct lrng_testing lrng_raw_sched_hires = {
+ .rb_reader = 0,
+ .rb_writer = ATOMIC_INIT(0),
+ .lock = __SPIN_LOCK_UNLOCKED(lrng_raw_sched_hires.lock),
+ .read_wait =
+ __WAIT_QUEUE_HEAD_INITIALIZER(lrng_raw_sched_hires.read_wait)
+};
+
+bool lrng_raw_sched_hires_entropy_store(u32 value)
+{
+ return lrng_testing_store(&lrng_raw_sched_hires, value,
+ &boot_raw_sched_hires_test);
+}
+
+static int lrng_raw_sched_hires_entropy_reader(u8 *outbuf, u32 outbuflen)
+{
+ return lrng_testing_reader(&lrng_raw_sched_hires,
+ &boot_raw_sched_hires_test,
+ outbuf, outbuflen);
+}
+
+static ssize_t lrng_raw_sched_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_sched_hires_entropy_reader);
+}
+
+static const struct file_operations lrng_raw_sched_hires_fops = {
+ .owner = THIS_MODULE,
+ .read = lrng_raw_sched_hires_read,
+};
+
+#endif /* CONFIG_LRNG_RAW_SCHED_HIRES_ENTROPY */
+
+/******************** Interrupt Performance Data Handling *********************/
+
+#ifdef CONFIG_LRNG_SCHED_PERF
+
+static u32 boot_sched_perf = 0;
+module_param(boot_sched_perf, uint, 0644);
+MODULE_PARM_DESC(boot_sched_perf, "Enable gathering scheduler-based entropy source performance data");
+
+static struct lrng_testing lrng_sched_perf = {
+ .rb_reader = 0,
+ .rb_writer = ATOMIC_INIT(0),
+ .lock = __SPIN_LOCK_UNLOCKED(lrng_sched_perf.lock),
+ .read_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lrng_sched_perf.read_wait)
+};
+
+bool lrng_sched_perf_time(u32 start)
+{
+ return lrng_testing_store(&lrng_sched_perf, random_get_entropy() - start,
+ &boot_sched_perf);
+}
+
+static int lrng_sched_perf_reader(u8 *outbuf, u32 outbuflen)
+{
+ return lrng_testing_reader(&lrng_sched_perf, &boot_sched_perf, outbuf,
+ outbuflen);
+}
+
+static ssize_t lrng_sched_perf_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ return lrng_testing_extract_user(file, to, count, ppos,
+ lrng_sched_perf_reader);
+}
+
+static const struct file_operations lrng_sched_perf_fops = {
+ .owner = THIS_MODULE,
+ .read = lrng_sched_perf_read,
+};
+
+#endif /* CONFIG_LRNG_SCHED_PERF */
+
+/*************** Raw Scheduler task_struct->pid Data Handling *****************/
+
+#ifdef CONFIG_LRNG_RAW_SCHED_PID_ENTROPY
+
+static u32 boot_raw_sched_pid_test = 0;
+module_param(boot_raw_sched_pid_test, uint, 0644);
+MODULE_PARM_DESC(boot_raw_sched_pid_test, "Enable gathering boot time entropy of the first PIDs collected by the scheduler entropy source");
+
+static struct lrng_testing lrng_raw_sched_pid = {
+ .rb_reader = 0,
+ .rb_writer = ATOMIC_INIT(0),
+ .lock = __SPIN_LOCK_UNLOCKED(lrng_raw_sched_pid.lock),
+ .read_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lrng_raw_sched_pid.read_wait)
+};
+
+bool lrng_raw_sched_pid_entropy_store(u32 value)
+{
+ return lrng_testing_store(&lrng_raw_sched_pid, value,
+ &boot_raw_sched_pid_test);
+}
+
+static int lrng_raw_sched_pid_entropy_reader(u8 *outbuf, u32 outbuflen)
+{
+ return lrng_testing_reader(&lrng_raw_sched_pid,
+ &boot_raw_sched_pid_test, outbuf, outbuflen);
+}
+
+static ssize_t lrng_raw_sched_pid_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ return lrng_testing_extract_user(file, to, count, ppos,
+ lrng_raw_sched_pid_entropy_reader);
+}
+
+static const struct file_operations lrng_raw_sched_pid_fops = {
+ .owner = THIS_MODULE,
+ .read = lrng_raw_sched_pid_read,
+};
+
+#endif /* CONFIG_LRNG_RAW_SCHED_PID_ENTROPY */
+
+
+/*********** Raw Scheduler task_struct->start_time Data Handling **************/
+
+#ifdef CONFIG_LRNG_RAW_SCHED_START_TIME_ENTROPY
+
+static u32 boot_raw_sched_starttime_test = 0;
+module_param(boot_raw_sched_starttime_test, uint, 0644);
+MODULE_PARM_DESC(boot_raw_sched_starttime_test, "Enable gathering boot time entropy of the first task start times collected by the scheduler entropy source");
+
+static struct lrng_testing lrng_raw_sched_starttime = {
+ .rb_reader = 0,
+ .rb_writer = ATOMIC_INIT(0),
+ .lock = __SPIN_LOCK_UNLOCKED(lrng_raw_sched_starttime.lock),
+ .read_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lrng_raw_sched_starttime.read_wait)
+};
+
+bool lrng_raw_sched_starttime_entropy_store(u32 value)
+{
+ return lrng_testing_store(&lrng_raw_sched_starttime, value,
+ &boot_raw_sched_starttime_test);
+}
+
+static int lrng_raw_sched_starttime_entropy_reader(u8 *outbuf, u32 outbuflen)
+{
+ return lrng_testing_reader(&lrng_raw_sched_starttime,
+ &boot_raw_sched_starttime_test, outbuf, outbuflen);
+}
+
+static ssize_t lrng_raw_sched_starttime_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ return lrng_testing_extract_user(file, to, count, ppos,
+ lrng_raw_sched_starttime_entropy_reader);
+}
+
+static const struct file_operations lrng_raw_sched_starttime_fops = {
+ .owner = THIS_MODULE,
+ .read = lrng_raw_sched_starttime_read,
+};
+
+#endif /* CONFIG_LRNG_RAW_SCHED_START_TIME_ENTROPY */
+
+/************** Raw Scheduler task_struct->nvcsw Data Handling ****************/
+
+#ifdef CONFIG_LRNG_RAW_SCHED_NVCSW_ENTROPY
+
+static u32 boot_raw_sched_nvcsw_test = 0;
+module_param(boot_raw_sched_nvcsw_test, uint, 0644);
+MODULE_PARM_DESC(boot_raw_sched_nvcsw_test, "Enable gathering boot time entropy of the first task context switch numbers collected by the scheduler entropy source");
+
+static struct lrng_testing lrng_raw_sched_nvcsw = {
+ .rb_reader = 0,
+ .rb_writer = ATOMIC_INIT(0),
+ .lock = __SPIN_LOCK_UNLOCKED(lrng_raw_sched_nvcsw.lock),
+ .read_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lrng_raw_sched_nvcsw.read_wait)
+};
+
+bool lrng_raw_sched_nvcsw_entropy_store(u32 value)
+{
+ return lrng_testing_store(&lrng_raw_sched_nvcsw, value,
+ &boot_raw_sched_nvcsw_test);
+}
+
+static int lrng_raw_sched_nvcsw_entropy_reader(u8 *outbuf, u32 outbuflen)
+{
+ return lrng_testing_reader(&lrng_raw_sched_nvcsw,
+ &boot_raw_sched_nvcsw_test, outbuf, outbuflen);
+}
+
+static ssize_t lrng_raw_sched_nvcsw_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ return lrng_testing_extract_user(file, to, count, ppos,
+ lrng_raw_sched_nvcsw_entropy_reader);
+}
+
+static const struct file_operations lrng_raw_sched_nvcsw_fops = {
+ .owner = THIS_MODULE,
+ .read = lrng_raw_sched_nvcsw_read,
+};
+
+#endif /* CONFIG_LRNG_RAW_SCHED_NVCSW_ENTROPY */
+
+/*********************************** 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_hash_cb *hash_cb = &lrng_sha_hash_cb;
+ ssize_t ret;
+
+ if (count > LRNG_ATOMIC_DIGEST_SIZE)
+ return -EINVAL;
+
+ ret = hash_cb->hash_init(shash, NULL) ?:
+ hash_cb->hash_update(shash, lrng_acvt_hash_data,
+ atomic_read_u32(&lrng_acvt_hash_data_size)) ?:
+ hash_cb->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_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_RAW_SCHED_HIRES_ENTROPY
+ debugfs_create_file_unsafe("lrng_raw_sched_hires", 0400,
+ lrng_raw_debugfs_root,
+ NULL, &lrng_raw_sched_hires_fops);
+#endif
+#ifdef CONFIG_LRNG_RAW_SCHED_PID_ENTROPY
+ debugfs_create_file_unsafe("lrng_raw_sched_pid", 0400,
+ lrng_raw_debugfs_root, NULL,
+ &lrng_raw_sched_pid_fops);
+#endif
+#ifdef CONFIG_LRNG_RAW_SCHED_START_TIME_ENTROPY
+ debugfs_create_file_unsafe("lrng_raw_sched_starttime", 0400,
+ lrng_raw_debugfs_root, NULL,
+ &lrng_raw_sched_starttime_fops);
+#endif
+#ifdef CONFIG_LRNG_RAW_SCHED_NVCSW_ENTROPY
+ debugfs_create_file_unsafe("lrng_raw_sched_nvcsw", 0400,
+ lrng_raw_debugfs_root, NULL,
+ &lrng_raw_sched_nvcsw_fops);
+#endif
+#ifdef CONFIG_LRNG_SCHED_PERF
+ debugfs_create_file_unsafe("lrng_sched_perf", 0400,
+ lrng_raw_debugfs_root, NULL,
+ &lrng_sched_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);
From 6c02564f6cdabbb5a463e0b10438d61a055e93d9 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Sun, 15 May 2022 18:13:56 +0200
Subject: [PATCH 20/25] 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
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 52 ++--
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_selftest.c | 397 ++++++++++++++++++++++++++++++
3 files changed, 424 insertions(+), 26 deletions(-)
create mode 100644 drivers/char/lrng/lrng_selftest.c
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -985,32 +985,32 @@ config LRNG_TEST_CPU_ES_COMPRESSION
than what may be required.
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
+
+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
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -27,3 +27,4 @@ obj-$(CONFIG_LRNG_JENT) += lrng_es_jen
obj-$(CONFIG_LRNG_HEALTH_TESTS) += lrng_health.o
obj-$(CONFIG_LRNG_TESTING) += lrng_testing.o
+obj-$(CONFIG_LRNG_SELFTEST) += lrng_selftest.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_selftest.c
@@ -0,0 +1,397 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG power-on and on-demand self-test
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+/*
+ * 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/lrng.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "lrng_drng_chacha20.h"
+#include "lrng_sha.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_TIMER_COMMON
+
+#include "lrng_es_timer_common.h"
+
+static u32 lrng_data_selftest_ptr = 0;
+static u32 lrng_data_selftest[LRNG_DATA_ARRAY_SIZE];
+
+static 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 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_data_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_TIMER_COMMON */
+
+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_TIMER_COMMON */
+
+/* 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_hash_cb *hash_cb = &lrng_sha_hash_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));
+
+ if (sizeof(digest) != hash_cb->hash_digestsize(NULL))
+ return LRNG_SEFLTEST_ERROR_HASH;
+
+ if (!hash_cb->hash_init(shash, NULL) &&
+ !hash_cb->hash_update(shash, hash_input,
+ sizeof(hash_input)) &&
+ !hash_cb->hash_final(shash, digest) &&
+ !memcmp(digest, lrng_hash_selftest_result, sizeof(digest)))
+ return 0;
+
+ pr_err("LRNG %s Hash self-test FAILED\n", hash_cb->hash_name());
+ return LRNG_SEFLTEST_ERROR_HASH;
+}
+
+#ifdef CONFIG_LRNG_DRNG_CHACHA20
+
+static 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 were generated using the ChaCha20 DRNG from
+ * https://www.chronox.de/chacha20.html
+ */
+static unsigned int lrng_chacha20_drng_selftest(void)
+{
+ const struct lrng_drng_cb *drng_cb = &lrng_cc20_drng_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 = drng_cb->drng_generate(&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 = drng_cb->drng_seed(&chacha20, seed, sizeof(expected_oneblock));
+ if (ret < 0)
+ goto err;
+ ret = drng_cb->drng_generate(&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 = drng_cb->drng_seed(&chacha20, seed,
+ sizeof(expected_block_nonalinged));
+ if (ret < 0)
+ goto err;
+ ret = drng_cb->drng_generate(&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;
+}
+
+#else /* CONFIG_LRNG_DRNG_CHACHA20 */
+
+static unsigned int lrng_chacha20_drng_selftest(void)
+{
+ return LRNG_SELFTEST_PASSED;
+}
+
+#endif /* CONFIG_LRNG_DRNG_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);
From 6a5b2149c0ba1f6a04ae69368e5fbed2b38bff96 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Sun, 31 Jul 2022 22:34:51 +0200
Subject: [PATCH 21/25] 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].
The sysctl implementation is only enabled if the existing random.c file
is not compiled.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_sysctl.c | 139 ++++++++++++++++++++++++++++++++
2 files changed, 140 insertions(+)
create mode 100644 drivers/char/lrng/lrng_sysctl.c
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_LRNG_SHA256) += lrng_sha25
obj-$(CONFIG_LRNG_SHA1) += lrng_sha1.o
obj-$(CONFIG_SYSCTL) += lrng_proc.o
+obj-$(CONFIG_LRNG_SYSCTL) += lrng_sysctl.o
obj-$(CONFIG_NUMA) += lrng_numa.o
obj-$(CONFIG_LRNG_SWITCH) += lrng_switch.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_sysctl.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG sysctl interfaces
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#include <linux/lrng.h>
+#include <linux/proc_fs.h>
+#include <linux/sysctl.h>
+#include <linux/uuid.h>
+
+#include "lrng_drng_mgr.h"
+#include "lrng_es_mgr.h"
+#include "lrng_sysctl.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_sysctl_do_uuid(const 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_sysctl_do_entropy(const struct ctl_table *table, int write,
+ void *buffer, size_t *lenp, loff_t *ppos)
+{
+ struct ctl_table fake_table;
+ int entropy_count = lrng_avail_entropy_aux();
+
+ fake_table.data = &entropy_count;
+ fake_table.maxlen = sizeof(entropy_count);
+
+ return proc_dointvec(&fake_table, write, buffer, lenp, ppos);
+}
+
+static int lrng_sysctl_do_poolsize(const struct ctl_table *table, int write,
+ void *buffer, size_t *lenp, loff_t *ppos)
+{
+ struct ctl_table fake_table;
+ u32 entropy_count = lrng_es[lrng_ext_es_aux]->max_entropy();
+
+ 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_sysctl_update_max_write_thresh(u32 new_digestsize)
+{
+ lrng_max_write_thresh = (int)new_digestsize;
+ /* Ensure that changes to the global variable are visible */
+ mb();
+}
+
+static struct ctl_table random_table[] = {
+ {
+ .procname = "poolsize",
+ .maxlen = sizeof(int),
+ .mode = 0444,
+ .proc_handler = lrng_sysctl_do_poolsize,
+ },
+ {
+ .procname = "entropy_avail",
+ .maxlen = sizeof(int),
+ .mode = 0444,
+ .proc_handler = lrng_sysctl_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_sysctl_do_uuid,
+ },
+ {
+ .procname = "uuid",
+ .maxlen = 16,
+ .mode = 0444,
+ .proc_handler = lrng_sysctl_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,
+ },
+};
+
+static int __init random_sysctls_init(void)
+{
+ register_sysctl_init("kernel/random", random_table);
+ return 0;
+}
+device_initcall(random_sysctls_init);
From fa6f7a3d77638110f79d1972668afc4b15254ffa Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Mon, 20 Feb 2023 22:12:04 +0100
Subject: [PATCH 22/25] LRMG - add drop-in replacement random(4) API
The LRNG is intended to be a full replacement of the existing random.c.
This also includes the providing of a full API and ABI compliant drop-in
replacement of all APIs offered by random.c.
These LRNG interfaces are compiled when the random.c is not compiled
any more.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/Makefile | 3 +-
drivers/char/lrng/Makefile | 5 +
drivers/char/lrng/lrng_interface_aux.c | 210 ++++++++++++
drivers/char/lrng/lrng_interface_dev_common.c | 315 ++++++++++++++++++
.../char/lrng/lrng_interface_random_kernel.c | 248 ++++++++++++++
.../char/lrng/lrng_interface_random_user.c | 104 ++++++
6 files changed, 884 insertions(+), 1 deletion(-)
create mode 100644 drivers/char/lrng/lrng_interface_aux.c
create mode 100644 drivers/char/lrng/lrng_interface_dev_common.c
create mode 100644 drivers/char/lrng/lrng_interface_random_kernel.c
create mode 100644 drivers/char/lrng/lrng_interface_random_user.c
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -3,7 +3,8 @@
# Makefile for the kernel character device drivers.
#
-obj-y += mem.o random.o
+obj-y += mem.o
+obj-$(CONFIG_RANDOM_DEFAULT_IMPL) += random.o
obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o
obj-y += misc.o
obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -29,3 +29,8 @@ obj-$(CONFIG_LRNG_JENT) += lrng_es_jen
obj-$(CONFIG_LRNG_HEALTH_TESTS) += lrng_health.o
obj-$(CONFIG_LRNG_TESTING) += lrng_testing.o
obj-$(CONFIG_LRNG_SELFTEST) += lrng_selftest.o
+
+obj-$(CONFIG_LRNG_COMMON_DEV_IF) += lrng_interface_dev_common.o
+obj-$(CONFIG_LRNG_RANDOM_IF) += lrng_interface_random_user.o \
+ lrng_interface_random_kernel.o \
+ lrng_interface_aux.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_interface_aux.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG auxiliary interfaces
+ *
+ * Copyright (C) 2022 Stephan Mueller <smueller@chronox.de>
+ * Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All
+ * Rights Reserved.
+ * Copyright (C) 2016 Jason Cooper <jason@lakedaemon.net>
+ */
+
+#include <linux/lrng.h>
+#include <linux/mm.h>
+#include <linux/random.h>
+
+#include "lrng_es_mgr.h"
+#include "lrng_interface_random_kernel.h"
+
+/*
+ * Fill a buffer with random numbers and tokenize it to provide random numbers
+ * to callers in fixed chunks. This approach is provided to be consistent with
+ * the Linux kernel interface requirements. Yet, this approach violate the
+ * backtracking resistance of the random number generator. Thus, the provided
+ * random numbers are not considered to be as strong as those requested directly
+ * from the LRNG.
+ */
+struct batched_entropy {
+ union {
+ u64 entropy_u64[LRNG_DRNG_BLOCKSIZE / sizeof(u64)];
+ u32 entropy_u32[LRNG_DRNG_BLOCKSIZE / sizeof(u32)];
+ u16 entropy_u16[LRNG_DRNG_BLOCKSIZE / sizeof(u16)];
+ u8 entropy_u8[LRNG_DRNG_BLOCKSIZE / sizeof(u8)];
+ };
+ 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_get_random_bytes(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_get_random_bytes(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);
+
+static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_u16) = {
+ .batch_lock = __SPIN_LOCK_UNLOCKED(batched_entropy_u16.lock),
+};
+
+u16 get_random_u16(void)
+{
+ u16 ret;
+ unsigned long flags;
+ struct batched_entropy *batch;
+
+ lrng_debug_report_seedlevel("get_random_u16");
+
+ batch = raw_cpu_ptr(&batched_entropy_u16);
+ spin_lock_irqsave(&batch->batch_lock, flags);
+ if (batch->position % ARRAY_SIZE(batch->entropy_u16) == 0) {
+ lrng_get_random_bytes(batch->entropy_u16, LRNG_DRNG_BLOCKSIZE);
+ batch->position = 0;
+ }
+ ret = batch->entropy_u16[batch->position++];
+ spin_unlock_irqrestore(&batch->batch_lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(get_random_u16);
+
+static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_u8) = {
+ .batch_lock = __SPIN_LOCK_UNLOCKED(batched_entropy_u8.lock),
+};
+
+u8 get_random_u8(void)
+{
+ u8 ret;
+ unsigned long flags;
+ struct batched_entropy *batch;
+
+ lrng_debug_report_seedlevel("get_random_u8");
+
+ batch = raw_cpu_ptr(&batched_entropy_u8);
+ spin_lock_irqsave(&batch->batch_lock, flags);
+ if (batch->position % ARRAY_SIZE(batch->entropy_u8) == 0) {
+ lrng_get_random_bytes(batch->entropy_u8, LRNG_DRNG_BLOCKSIZE);
+ batch->position = 0;
+ }
+ ret = batch->entropy_u8[batch->position++];
+ spin_unlock_irqrestore(&batch->batch_lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(get_random_u8);
+
+/* Taken directly from random.c */
+u32 __get_random_u32_below(u32 ceil)
+{
+ u64 mult = (u64)ceil * get_random_u32();
+
+ if (unlikely((u32)mult < ceil)) {
+ u32 bound = -ceil % ceil;
+ while (unlikely((u32)mult < bound))
+ mult = (u64)ceil * get_random_u32();
+ }
+ return mult >> 32;
+}
+EXPORT_SYMBOL(__get_random_u32_below);
+
+#ifdef CONFIG_SMP
+/*
+ * This function is called when the CPU is coming up, with entry
+ * CPUHP_RANDOM_PREPARE, which comes before CPUHP_WORKQUEUE_PREP.
+ */
+int random_prepare_cpu(unsigned int cpu)
+{
+ /*
+ * When the cpu comes back online, immediately invalidate all batches,
+ * so that we serve fresh randomness.
+ */
+ per_cpu_ptr(&batched_entropy_u8, cpu)->position = 0;
+ per_cpu_ptr(&batched_entropy_u16, cpu)->position = 0;
+ per_cpu_ptr(&batched_entropy_u32, cpu)->position = 0;
+ per_cpu_ptr(&batched_entropy_u64, cpu)->position = 0;
+ return 0;
+}
+
+int random_online_cpu(unsigned int cpu)
+{
+ return 0;
+}
+#endif
+
+/*
+ * 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_u8, cpu);
+ spin_lock_irqsave(&batched_entropy->batch_lock, flags);
+ batched_entropy->position = 0;
+ spin_unlock_irqrestore(&batched_entropy->batch_lock, flags);
+
+ batched_entropy = per_cpu_ptr(&batched_entropy_u16, cpu);
+ spin_lock_irqsave(&batched_entropy->batch_lock, flags);
+ batched_entropy->position = 0;
+ spin_unlock_irqrestore(&batched_entropy->batch_lock, flags);
+
+ batched_entropy = per_cpu_ptr(&batched_entropy_u32, cpu);
+ spin_lock_irqsave(&batched_entropy->batch_lock, flags);
+ batched_entropy->position = 0;
+ spin_unlock_irqrestore(&batched_entropy->batch_lock, flags);
+
+ 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);
+ }
+}
--- /dev/null
+++ b/drivers/char/lrng/lrng_interface_dev_common.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG User and kernel space interfaces
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/random.h>
+#include <linux/slab.h>
+
+#include "lrng_drng_mgr.h"
+#include "lrng_es_aux.h"
+#include "lrng_es_mgr.h"
+#include "lrng_interface_dev_common.h"
+
+DECLARE_WAIT_QUEUE_HEAD(lrng_write_wait);
+static struct fasync_struct *fasync;
+
+static bool lrng_seed_hw = true; /* Allow HW to provide seed */
+static bool lrng_seed_user = true; /* Allow user space to provide seed */
+
+/********************************** Helper ***********************************/
+
+static u32 lrng_get_aux_ent(void)
+{
+ return lrng_es[lrng_ext_es_aux]->curr_entropy(0);
+}
+
+/* Is the DRNG seed level too low? */
+bool lrng_need_entropy(void)
+{
+ return (lrng_get_aux_ent() < 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_dev(void)
+{
+ kill_fasync(&fasync, SIGIO, POLL_IN);
+}
+
+/* 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_seed_hw;
+ return 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 the LRNG is not yet operational, allow all entropy sources
+ * to deliver data unconditionally to get fully seeded asap.
+ */
+ if (!lrng_state_operational())
+ return;
+
+ if (source == lrng_noise_source_hw)
+ lrng_seed_hw = type;
+ else
+ lrng_seed_user = type;
+}
+
+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);
+}
+
+/************************ LRNG user output interfaces *************************/
+
+ssize_t lrng_read_seed(char __user *buf, size_t nbytes, unsigned int flags)
+{
+ ssize_t ret = 0;
+ u64 t[(sizeof(struct entropy_buf) + 3 * sizeof(u64) - 1) / sizeof(u64)];
+
+ memset(t, 0, sizeof(t));
+ ret = lrng_get_seed(t, min_t(size_t, nbytes, sizeof(t)), flags);
+ if (ret == -EMSGSIZE && copy_to_user(buf, t, sizeof(u64)))
+ ret = -EFAULT;
+ else if (ret > 0 && copy_to_user(buf, t, ret))
+ ret = -EFAULT;
+
+ memzero_explicit(t, sizeof(t));
+
+ return ret;
+}
+
+ssize_t lrng_read_common(char __user *buf, size_t nbytes, bool pr)
+{
+ 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 (!IS_ENABLED(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, pr);
+ 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;
+}
+
+ssize_t lrng_read_common_block(int nonblock, int pr,
+ char __user *buf, size_t nbytes)
+{
+ int ret;
+
+ if (nbytes == 0)
+ return 0;
+
+ ret = lrng_drng_sleep_while_nonoperational(nonblock);
+ if (ret)
+ return ret;
+
+ return lrng_read_common(buf, nbytes, !!pr);
+}
+
+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,
+ file->f_flags & O_SYNC, buf, nbytes);
+}
+
+__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;
+}
+
+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()) {
+ ret = lrng_drng_initalize();
+ if (!ret)
+ return ret;
+ }
+
+ 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;
+}
+
+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);
+}
+
+long lrng_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+ u32 digestsize_bits;
+ int size, ent_count_bits, ret;
+ int __user *p = (int __user *)arg;
+
+ switch (cmd) {
+ case RNDGETENTCNT:
+ ent_count_bits = lrng_avail_entropy_aux();
+ 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_get_aux_ent() + 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);
+ ret = lrng_drng_write_common((const char __user *)p, size,
+ ent_count_bits);
+ return (ret < 0) ? ret : 0;
+ 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;
+ }
+}
+EXPORT_SYMBOL(lrng_ioctl);
+
+int lrng_fasync(int fd, struct file *filp, int on)
+{
+ return fasync_helper(fd, filp, on, &fasync);
+}
--- /dev/null
+++ b/drivers/char/lrng/lrng_interface_random_kernel.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG Kernel space interfaces API/ABI compliant to linux/random.h
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/blkdev.h>
+#include <linux/hw_random.h>
+#include <linux/kthread.h>
+#include <linux/lrng.h>
+#include <linux/random.h>
+
+#include "lrng_es_aux.h"
+#include "lrng_es_irq.h"
+#include "lrng_es_mgr.h"
+#include "lrng_interface_dev_common.h"
+#include "lrng_interface_random_kernel.h"
+
+static ATOMIC_NOTIFIER_HEAD(random_ready_notifier);
+
+/********************************** Helper ***********************************/
+
+static bool lrng_trust_bootloader __initdata =
+ IS_ENABLED(CONFIG_RANDOM_TRUST_BOOTLOADER);
+
+static int __init lrng_parse_trust_bootloader(char *arg)
+{
+ return kstrtobool(arg, &lrng_trust_bootloader);
+}
+early_param("random.trust_bootloader", lrng_parse_trust_bootloader);
+
+void __init random_init_early(const char *command_line)
+{
+ lrng_rand_initialize_early();
+ lrng_pool_insert_aux(command_line, strlen(command_line), 0);
+}
+
+void __init random_init(void)
+{
+ lrng_rand_initialize();
+}
+
+/*
+ * Add a callback function that will be invoked when the LRNG is initialised,
+ * or immediately if it already has been. Only use this is you are absolutely
+ * sure it is required. Most users should instead be able to test
+ * `rng_is_initialized()` on demand, or make use of `get_random_bytes_wait()`.
+ */
+int __cold execute_with_initialized_rng(struct notifier_block *nb)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&random_ready_notifier.lock, flags);
+ if (rng_is_initialized())
+ nb->notifier_call(nb, 0, NULL);
+ else
+ ret = raw_notifier_chain_register(
+ (struct raw_notifier_head *)&random_ready_notifier.head,
+ nb);
+ spin_unlock_irqrestore(&random_ready_notifier.lock, flags);
+ return ret;
+}
+
+void lrng_kick_random_ready(void)
+{
+ atomic_notifier_call_chain(&random_ready_notifier, 0, NULL);
+}
+
+/************************ 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 void *buffer, size_t count,
+ size_t entropy_bits, bool sleep_after)
+{
+ /*
+ * Suspend writing if we are fully loaded with entropy or if caller
+ * did not provide any 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() && entropy_bits) ||
+ lrng_state_exseed_allow(lrng_noise_source_hw) ||
+ !sleep_after ||
+ 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 __init add_bootloader_randomness(const void *buf, size_t size)
+{
+ lrng_pool_insert_aux(buf, size, lrng_trust_bootloader ? size * 8 : 0);
+}
+
+/*
+ * 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_irq_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, size_t 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) { }
+EXPORT_SYMBOL(add_interrupt_randomness);
+#endif
+
+#if IS_ENABLED(CONFIG_VMGENID)
+static BLOCKING_NOTIFIER_HEAD(lrng_vmfork_chain);
+
+/*
+ * Handle a new unique VM ID, which is unique, not secret, so we
+ * don't credit it, but we do immediately force a reseed after so
+ * that it's used by the crng posthaste.
+ */
+void add_vmfork_randomness(const void *unique_vm_id, size_t size)
+{
+ add_device_randomness(unique_vm_id, size);
+ if (lrng_state_operational())
+ lrng_drng_force_reseed();
+ blocking_notifier_call_chain(&lrng_vmfork_chain, 0, NULL);
+}
+#if IS_MODULE(CONFIG_VMGENID)
+EXPORT_SYMBOL_GPL(add_vmfork_randomness);
+#endif
+
+int register_random_vmfork_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&lrng_vmfork_chain, nb);
+}
+EXPORT_SYMBOL_GPL(register_random_vmfork_notifier);
+
+int unregister_random_vmfork_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&lrng_vmfork_chain, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_random_vmfork_notifier);
+#endif
+
+/*********************** 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, size_t nbytes)
+{
+ lrng_get_random_bytes(buf, nbytes);
+}
+EXPORT_SYMBOL(get_random_bytes);
+
+/*
+ * 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)
+{
+ return lrng_drng_sleep_while_non_min_seeded();
+}
+EXPORT_SYMBOL(wait_for_random_bytes);
+
+/*
+ * 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);
--- /dev/null
+++ b/drivers/char/lrng/lrng_interface_random_user.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG Common user space interfaces compliant to random(4), random(7) and
+ * getrandom(2) man pages.
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/random.h>
+#include <linux/syscalls.h>
+
+#include "lrng_es_mgr.h"
+#include "lrng_interface_dev_common.h"
+
+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, false);
+}
+
+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,
+};
+
+/*
+ * GRND_SEED
+ *
+ * This flag requests to provide the data directly from the entropy sources.
+ *
+ * The behavior of the call is exactly as outlined for the function
+ * lrng_get_seed in lrng.h.
+ */
+#define GRND_SEED 0x0010
+
+/*
+ * GRND_FULLY_SEEDED
+ *
+ * This flag indicates whether the caller wants to reseed a DRNG that is already
+ * fully seeded. See esdm_get_seed in lrng.h for details.
+ */
+#define GRND_FULLY_SEEDED 0x0020
+
+SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count,
+ unsigned int, flags)
+{
+ if (flags & ~(GRND_NONBLOCK|GRND_RANDOM|GRND_INSECURE|
+ GRND_SEED|GRND_FULLY_SEEDED))
+ 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 ((flags &
+ (GRND_INSECURE|GRND_SEED)) == (GRND_INSECURE|GRND_SEED))
+ return -EINVAL;
+ if ((flags &
+ (GRND_RANDOM|GRND_SEED)) == (GRND_RANDOM|GRND_SEED))
+ return -EINVAL;
+
+ if (count > INT_MAX)
+ count = INT_MAX;
+
+ if (flags & GRND_INSECURE) {
+ return lrng_drng_read(NULL, buf, count, NULL);
+ } else if (flags & GRND_SEED) {
+ unsigned int seed_flags = (flags & GRND_NONBLOCK) ?
+ LRNG_GET_SEED_NONBLOCK : 0;
+
+ seed_flags |= (flags & GRND_FULLY_SEEDED) ?
+ LRNG_GET_SEED_FULLY_SEEDED : 0;
+ return lrng_read_seed(buf, count, seed_flags);
+ }
+
+ return lrng_read_common_block(flags & GRND_NONBLOCK,
+ flags & GRND_RANDOM, buf, count);
+}
From 021ba8b87e270abdb892ae853fc863cb7e258265 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Sun, 9 Oct 2022 10:22:39 +0200
Subject: [PATCH 23/25] LRNG - add kernel crypto API interface
The LRNG can be registered with the kernel crypto API's random number
generator framework. This offers a random number generator with the name
"lrng" and a priority that is intended to be higher than the existing
RNG implementations.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 26 ++---
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_interface_kcapi.c | 129 +++++++++++++++++++++++
3 files changed, 143 insertions(+), 13 deletions(-)
create mode 100644 drivers/char/lrng/lrng_interface_kcapi.c
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -89,18 +89,18 @@ config LRNG_AIS2031_NTG1_SEEDING_STRATEG
endmenu # "Specific DRNG seeding strategies"
-# menu "LRNG Interfaces"
-#
-# config LRNG_KCAPI_IF
-# tristate "Interface with Kernel Crypto API"
-# depends on CRYPTO_RNG
-# help
-# The LRNG can be registered with the kernel crypto API's
-# random number generator framework. This offers a random
-# number generator with the name "lrng" and a priority that
-# is intended to be higher than the existing RNG
-# implementations.
-#
+menu "LRNG Interfaces"
+
+config LRNG_KCAPI_IF
+ tristate "Interface with Kernel Crypto API"
+ depends on CRYPTO_RNG
+ help
+ The LRNG can be registered with the kernel crypto API's
+ random number generator framework. This offers a random
+ number generator with the name "lrng" and a priority that
+ is intended to be higher than the existing RNG
+ implementations.
+
# config LRNG_HWRAND_IF
# tristate "Interface with Hardware Random Number Generator Framework"
# depends on HW_RANDOM
@@ -120,7 +120,7 @@ endmenu # "Specific DRNG seeding strateg
# identically to /dev/random including IOCTL, read and write
# operations.
#
-# endmenu # "LRNG Interfaces"
+endmenu # "LRNG Interfaces"
menu "Entropy Source Configuration"
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -34,3 +34,4 @@ obj-$(CONFIG_LRNG_COMMON_DEV_IF) += lrng
obj-$(CONFIG_LRNG_RANDOM_IF) += lrng_interface_random_user.o \
lrng_interface_random_kernel.o \
lrng_interface_aux.o
+obj-$(CONFIG_LRNG_KCAPI_IF) += lrng_interface_kcapi.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_interface_kcapi.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG interface with the RNG framework of the kernel crypto API
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#include <linux/lrng.h>
+#include <linux/module.h>
+#include <crypto/internal/rng.h>
+
+#include "lrng_drng_mgr.h"
+#include "lrng_es_aux.h"
+
+static int lrng_kcapi_if_init(struct crypto_tfm *tfm)
+{
+ return 0;
+}
+
+static void lrng_kcapi_if_cleanup(struct crypto_tfm *tfm) { }
+
+static int lrng_kcapi_if_reseed(const u8 *src, unsigned int slen)
+{
+ int ret;
+
+ if (!slen)
+ return 0;
+
+ /* Insert caller-provided data without crediting entropy */
+ ret = lrng_pool_insert_aux((u8 *)src, slen, 0);
+ if (ret)
+ return ret;
+
+ /* Make sure the new data is immediately available to DRNG */
+ lrng_drng_force_reseed();
+
+ return 0;
+}
+
+static int lrng_kcapi_if_random(struct crypto_rng *tfm,
+ const u8 *src, unsigned int slen,
+ u8 *rdata, unsigned int dlen)
+{
+ int ret = lrng_kcapi_if_reseed(src, slen);
+
+ if (!ret)
+ lrng_get_random_bytes_full(rdata, dlen);
+
+ return ret;
+}
+
+static int lrng_kcapi_if_reset(struct crypto_rng *tfm,
+ const u8 *seed, unsigned int slen)
+{
+ return lrng_kcapi_if_reseed(seed, slen);
+}
+
+static struct rng_alg lrng_alg = {
+ .generate = lrng_kcapi_if_random,
+ .seed = lrng_kcapi_if_reset,
+ .seedsize = 0,
+ .base = {
+ .cra_name = "stdrng",
+ .cra_driver_name = "lrng",
+ .cra_priority = 500,
+ .cra_ctxsize = 0,
+ .cra_module = THIS_MODULE,
+ .cra_init = lrng_kcapi_if_init,
+ .cra_exit = lrng_kcapi_if_cleanup,
+
+ }
+};
+
+#ifdef CONFIG_LRNG_DRNG_ATOMIC
+static int lrng_kcapi_if_random_atomic(struct crypto_rng *tfm,
+ const u8 *src, unsigned int slen,
+ u8 *rdata, unsigned int dlen)
+{
+ int ret = lrng_kcapi_if_reseed(src, slen);
+
+ if (!ret)
+ lrng_get_random_bytes(rdata, dlen);
+
+ return ret;
+}
+
+static struct rng_alg lrng_alg_atomic = {
+ .generate = lrng_kcapi_if_random_atomic,
+ .seed = lrng_kcapi_if_reset,
+ .seedsize = 0,
+ .base = {
+ .cra_name = "lrng_atomic",
+ .cra_driver_name = "lrng_atomic",
+ .cra_priority = 100,
+ .cra_ctxsize = 0,
+ .cra_module = THIS_MODULE,
+ .cra_init = lrng_kcapi_if_init,
+ .cra_exit = lrng_kcapi_if_cleanup,
+
+ }
+};
+#endif /* CONFIG_LRNG_DRNG_ATOMIC */
+
+static int __init lrng_kcapi_if_mod_init(void)
+{
+ return
+#ifdef CONFIG_LRNG_DRNG_ATOMIC
+ crypto_register_rng(&lrng_alg_atomic) ?:
+#endif
+ crypto_register_rng(&lrng_alg);
+}
+
+static void __exit lrng_kcapi_if_mod_exit(void)
+{
+ crypto_unregister_rng(&lrng_alg);
+#ifdef CONFIG_LRNG_DRNG_ATOMIC
+ crypto_unregister_rng(&lrng_alg_atomic);
+#endif
+}
+
+module_init(lrng_kcapi_if_mod_init);
+module_exit(lrng_kcapi_if_mod_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
+MODULE_DESCRIPTION("Entropy Source and DRNG Manager kernel crypto API RNG framework interface");
+MODULE_ALIAS_CRYPTO("lrng");
+MODULE_ALIAS_CRYPTO("lrng_atomic");
+MODULE_ALIAS_CRYPTO("stdrng");
From 7697fe0de6bdc7a8e0a4c722dbf6dec24ffe53d5 Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Sun, 15 May 2022 18:39:30 +0200
Subject: [PATCH 24/25] LRNG - add /dev/lrng device file support
The LRNG can create a character device file that operates identically
to /dev/random including IOCTL, read and write operations.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 18 ++++++-------
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_interface_dev.c | 35 ++++++++++++++++++++++++++
3 files changed, 45 insertions(+), 9 deletions(-)
create mode 100644 drivers/char/lrng/lrng_interface_dev.c
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -111,15 +111,15 @@ config LRNG_KCAPI_IF
# with the name "lrng" that is accessible via the framework.
# For example it allows pulling data from the LRNG via the
# /dev/hwrng file.
-#
-# config LRNG_DEV_IF
-# bool "Character device file interface"
-# select LRNG_COMMON_DEV_IF
-# help
-# The LRNG can create a character device file that operates
-# identically to /dev/random including IOCTL, read and write
-# operations.
-#
+
+config LRNG_DEV_IF
+ bool "Character device file interface"
+ select LRNG_COMMON_DEV_IF
+ help
+ The LRNG can create a character device file that operates
+ identically to /dev/random including IOCTL, read and write
+ operations.
+
endmenu # "LRNG Interfaces"
menu "Entropy Source Configuration"
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -35,3 +35,4 @@ obj-$(CONFIG_LRNG_RANDOM_IF) += lrng_in
lrng_interface_random_kernel.o \
lrng_interface_aux.o
obj-$(CONFIG_LRNG_KCAPI_IF) += lrng_interface_kcapi.o
+obj-$(CONFIG_LRNG_DEV_IF) += lrng_interface_dev.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_interface_dev.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG user space device file interface
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+
+#include "lrng_interface_dev_common.h"
+
+static const struct file_operations lrng_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,
+};
+
+static struct miscdevice lrng_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "lrng",
+ .nodename = "lrng",
+ .fops = &lrng_fops,
+ .mode = 0666
+};
+
+static int __init lrng_dev_if_mod_init(void)
+{
+ return misc_register(&lrng_miscdev);
+}
+device_initcall(lrng_dev_if_mod_init);
From 243b20ab41748e898abcf298b0cb836f04391afd Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Sun, 15 May 2022 18:43:30 +0200
Subject: [PATCH 25/25] LRNG - add hwrand framework interface
The LRNG can be registered with the hardware random number generator
framework. This offers a random number generator with the name "lrng"
that is accessible via the framework. For example it allows pulling
data from the LRNG via the /dev/hwrng file.
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
drivers/char/lrng/Kconfig | 20 +++----
drivers/char/lrng/Makefile | 1 +
drivers/char/lrng/lrng_interface_hwrand.c | 68 +++++++++++++++++++++++
3 files changed, 79 insertions(+), 10 deletions(-)
create mode 100644 drivers/char/lrng/lrng_interface_hwrand.c
--- a/drivers/char/lrng/Kconfig
+++ b/drivers/char/lrng/Kconfig
@@ -101,16 +101,16 @@ config LRNG_KCAPI_IF
is intended to be higher than the existing RNG
implementations.
-# config LRNG_HWRAND_IF
-# tristate "Interface with Hardware Random Number Generator Framework"
-# depends on HW_RANDOM
-# select LRNG_DRNG_ATOMIC
-# help
-# The LRNG can be registered with the hardware random number
-# generator framework. This offers a random number generator
-# with the name "lrng" that is accessible via the framework.
-# For example it allows pulling data from the LRNG via the
-# /dev/hwrng file.
+config LRNG_HWRAND_IF
+ tristate "Interface with Hardware Random Number Generator Framework"
+ depends on HW_RANDOM
+ select LRNG_DRNG_ATOMIC
+ help
+ The LRNG can be registered with the hardware random number
+ generator framework. This offers a random number generator
+ with the name "lrng" that is accessible via the framework.
+ For example it allows pulling data from the LRNG via the
+ /dev/hwrng file.
config LRNG_DEV_IF
bool "Character device file interface"
--- a/drivers/char/lrng/Makefile
+++ b/drivers/char/lrng/Makefile
@@ -36,3 +36,4 @@ obj-$(CONFIG_LRNG_RANDOM_IF) += lrng_in
lrng_interface_aux.o
obj-$(CONFIG_LRNG_KCAPI_IF) += lrng_interface_kcapi.o
obj-$(CONFIG_LRNG_DEV_IF) += lrng_interface_dev.o
+obj-$(CONFIG_LRNG_HWRAND_IF) += lrng_interface_hwrand.o
--- /dev/null
+++ b/drivers/char/lrng/lrng_interface_hwrand.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * LRNG interface with the HW-Random framework
+ *
+ * Copyright (C) 2022, Stephan Mueller <smueller@chronox.de>
+ */
+
+#include <linux/lrng.h>
+#include <linux/hw_random.h>
+#include <linux/module.h>
+
+static int lrng_hwrand_if_random(struct hwrng *rng, void *buf, size_t max,
+ bool wait)
+{
+ /*
+ * lrng_get_random_bytes_full not called as we cannot block.
+ *
+ * Note: We should either adjust .quality below depending on
+ * rng_is_initialized() or block here, but neither is not supported by
+ * the hw_rand framework.
+ */
+ lrng_get_random_bytes(buf, max);
+ return (int)max;
+}
+
+static struct hwrng lrng_hwrand = {
+ .name = "lrng",
+ .init = NULL,
+ .cleanup = NULL,
+ .read = lrng_hwrand_if_random,
+
+ /*
+ * We set .quality only in case the LRNG does not provide the common
+ * interfaces or does not use the legacy RNG as entropy source. This
+ * shall avoid that the LRNG automatically spawns the hw_rand
+ * framework's hwrng kernel thread to feed data into
+ * add_hwgenerator_randomness. When the LRNG implements the common
+ * interfaces, this function feeds the data directly into the LRNG.
+ * If the LRNG uses the legacy RNG as entropy source,
+ * add_hwgenerator_randomness is implemented by the legacy RNG, but
+ * still eventually feeds the data into the LRNG. We should avoid such
+ * circular loops.
+ *
+ * We can specify full entropy here, because the LRNG is designed
+ * to provide full entropy.
+ */
+#if !defined(CONFIG_LRNG_RANDOM_IF) && \
+ !defined(CONFIG_LRNG_KERNEL_RNG)
+ .quality = 1024,
+#endif
+};
+
+static int __init lrng_hwrand_if_mod_init(void)
+{
+ return hwrng_register(&lrng_hwrand);
+}
+
+static void __exit lrng_hwrand_if_mod_exit(void)
+{
+ hwrng_unregister(&lrng_hwrand);
+}
+
+module_init(lrng_hwrand_if_mod_init);
+module_exit(lrng_hwrand_if_mod_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
+MODULE_DESCRIPTION("Entropy Source and DRNG Manager HW-Random Interface");