random(4): Block read_random(9) on initial seeding

read_random() is/was used, mostly without error checking, in a lot of
very sensitive places in the kernel -- including seeding the widely used
arc4random(9).

Most uses, especially arc4random(9), should block until the device is seeded
rather than proceeding with a bogus or empty seed.  I did not spy any
obvious kernel consumers where blocking would be inappropriate (in the
sense that lack of entropy would be ok -- I did not investigate locking
angle thoroughly).  In many instances, arc4random_buf(9) or that family
of APIs would be more appropriate anyway; that work was done in r345865.

A minor cleanup was made to the implementation of the READ_RANDOM function:
instead of using a variable-length array on the stack to temporarily store
all full random blocks sufficient to satisfy the requested 'len', only store
a single block on the stack.  This has some benefit in terms of reducing
stack usage, reducing memcpy overhead and reducing devrandom output leakage
via the stack.  Additionally, the stack block is now safely zeroed if it was
used.

One caveat of this change is that the kern.arandom sysctl no longer returns
zero bytes immediately if the random device is not seeded.  This means that
FreeBSD-specific userspace applications which attempted to handle an
unseeded random device may be broken by this change.  If such behavior is
needed, it can be replaced by the more portable getrandom(2) GRND_NONBLOCK
option.

On any typical FreeBSD system, entropy is persisted on read/write media and
used to seed the random device very early in boot, and blocking is never a
problem.

This change primarily impacts the behavior of /dev/random on embedded
systems with read-only media that do not configure "nodevice random".  We
toggle the default from 'charge on blindly with no entropy' to 'block
indefinitely.'  This default is safer, but may cause frustration.  Embedded
system designers using FreeBSD have several options.  The most obvious is to
plan to have a small writable NVRAM or NAND to persist entropy, like larger
systems.  Early entropy can be fed from any loader, or by writing directly
to /dev/random during boot.  Some embedded SoCs now provide a fast hardware
entropy source; this would also work for quickly seeding Fortuna.  A 3rd
option would be creating an embedded-specific, more simplistic random
module, like that designed by DJB in [1] (this design still requires a small
rewritable media for forward secrecy).  Finally, the least preferred option
might be "nodevice random", although I plan to remove this in a subsequent
revision.

To help developers emulate the behavior of these embedded systems on
ordinary workstations, the tunable kern.random.block_seeded_status was
added.  When set to 1, it blocks the random device.

I attempted to document this change in random.4 and random.9 and ran into a
bunch of out-of-date or irrelevant or inaccurate content and ended up
rototilling those documents more than I intended to.  Sorry.  I think
they're in a better state now.

PR:		230875
Reviewed by:	delphij, markm (earlier version)
Approved by:	secteam(delphij), devrandom(markm)
Relnotes:	yes
Differential Revision:	https://reviews.freebsd.org/D19744
This commit is contained in:
Conrad Meyer 2019-04-15 18:40:36 +00:00
parent 16fba2f38a
commit 13774e8228
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=346250
10 changed files with 337 additions and 426 deletions

View File

@ -23,7 +23,7 @@
.\" .\"
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd August 26, 2018 .Dd April 15, 2019
.Dt RANDOM 4 .Dt RANDOM 4
.Os .Os
.Sh NAME .Sh NAME
@ -32,62 +32,43 @@
.Sh SYNOPSIS .Sh SYNOPSIS
.Cd "device random" .Cd "device random"
.Cd "options RANDOM_LOADABLE" .Cd "options RANDOM_LOADABLE"
.Cd "options RANDOM_ENABLE_ETHER"
.Cd "options RANDOM_ENABLE_UMA" .Cd "options RANDOM_ENABLE_UMA"
.Sh DESCRIPTION .Sh DESCRIPTION
The The
.Nm .Nm
device device returns an endless supply of random bytes when read.
returns an endless supply of random bytes when read.
It also accepts and reads data
as any ordinary file.
.Pp .Pp
The generator will start in an The generator will start in an
.Em unseeded .Em unseeded
state, and will block reads until state, and will block reads until it is seeded for the first time.
it is seeded for the first time.
This may cause trouble at system boot
when keys and the like
are generated from
.Nm
so steps should be taken to ensure a
seeding as soon as possible.
.Pp .Pp
It is also possible To provide prompt access to the random device at boot time,
to read random bytes .Fx
by using the KERN_ARND sysctl. automatically persists some entropy data in
On the command line .Pa /boot/entropy
this could be done by for the loader to provide to the kernel.
Additional entropy is regularly saved in
.Pa /var/db/entropy .
This saved entropy is sufficient to unblock the random device on devices with
writeable media.
.Pp .Pp
.Dl "sysctl -x -B 16 kern.arandom" Embedded applications without writable media must determine their own scheme
for re-seeding the random device on boot, or accept that the device
will remain unseeded and block reads indefinitely.
See
.Sx SECURITY CONSIDERATIONS
for more detail.
.Pp .Pp
This sysctl will not return In addition to
random bytes unless .Xr read 2 ,
the the direct output of the abstract kernel entropy device can be read with
.Nm .Xr getrandom 2 ,
device is seeded. .Xr getentropy 3 ,
.Pp or the
This initial seeding .Xr sysctl 8
of random number generators pseudo-variable
is a bootstrapping problem .Va kern.arandom .
that needs very careful attention.
In some cases,
it may be difficult
to find enough randomness
to seed a random number generator
until a system is fully operational,
but the system requires random numbers
to become fully operational.
It is (or more accurately should be)
critically important that the
.Nm
device is seeded
before the first time it is used.
In the case where a dummy or "blocking-only"
device is used,
it is the responsibility
of the system architect
to ensure that no blocking reads
hold up critical processes.
.Pp .Pp
To see the current settings of the software To see the current settings of the software
.Nm .Nm
@ -97,17 +78,20 @@ device, use the command line:
.Pp .Pp
which results in something like: which results in something like:
.Bd -literal -offset indent .Bd -literal -offset indent
kern.random.block_seeded_status: 0
kern.random.fortuna.minpoolsize: 64 kern.random.fortuna.minpoolsize: 64
kern.random.harvest.mask_symbolic: [HIGH_PERFORMANCE], ... ,CACHED kern.random.harvest.mask_symbolic: ENABLEDSOURCE,[DISABLEDSOURCE],...,CACHED
kern.random.harvest.mask_bin: 00111111111 kern.random.harvest.mask_bin: 00000010000000111011111
kern.random.harvest.mask: 511 kern.random.harvest.mask: 66015
kern.random.use_chacha20_cipher: 0
kern.random.random_sources: 'Intel Secure Key RNG' kern.random.random_sources: 'Intel Secure Key RNG'
.Ed .Ed
.Pp .Pp
Other than Other than
.Dl kern.random.fortuna.minpoolsize .Va kern.random.block_seeded_status ,
.Va kern.random.fortuna.minpoolsize ,
and and
.Dl kern.random.harvest.mask .Va kern.random.harvest.mask ,
all settings are read-only. all settings are read-only.
.Pp .Pp
The The
@ -137,176 +121,22 @@ and
.Va kern.random.harvest.mask_symbolic .Va kern.random.harvest.mask_symbolic
sysctls sysctls
can be used to confirm can be used to confirm
that the choices are correct. settings in a human readable form.
Note that disabled items Disabled items
in the latter item in the latter item
are listed in square brackets. are listed in square brackets.
See See
.Xr random_harvest 9 .Xr random_harvest 9
for more on the harvesting of entropy. for more on the harvesting of entropy.
.Pp
When
.Cd "options RANDOM_LOADABLE"
is used,
the
.Pa /dev/random
device is not created
until an "algorithm module"
is loaded.
The only module built by default is
.Em random_fortuna .
The
.Em random_yarrow
module was removed in
.Fx 12 .
Note that this loadable module
is slightly less efficient
than its compiled-in equivalent.
This is because some functions
must be locked against
load and unload events,
and also must be indirect calls
to allow for removal.
.Pp
When
.Cd "options RANDOM_ENABLE_UMA"
is used,
the
.Pa /dev/random
device will obtain entropy
from the zone allocator.
This is potentially very high rate,
and if so will be of questionable use.
If this is the case,
use of this option
is not recommended.
Determining this is not trivial,
so experimenting and measurement
using tools such as
.Xr dtrace 1
will be required.
.Sh RANDOMNESS
The use of randomness in the field of computing
is a rather subtle issue because randomness means
different things to different people.
Consider generating a password randomly,
simulating a coin tossing experiment or
choosing a random back-off period when a server does not respond.
Each of these tasks requires random numbers,
but the random numbers in each case have different requirements.
.Pp
Generation of passwords, session keys and the like
requires cryptographic randomness.
A cryptographic random number generator should be designed
so that its output is difficult to guess,
even if a lot of auxiliary information is known
(such as when it was seeded, subsequent or previous output, and so on).
On
.Fx ,
seeding for cryptographic random number generators is provided by the
.Nm
device,
which provides real randomness.
The
.Xr arc4random 3
library call provides a pseudo-random sequence
which is generally reckoned to be suitable for
simple cryptographic use.
The OpenSSL library also provides functions for managing randomness
via functions such as
.Xr RAND_bytes 3
and
.Xr RAND_add 3 .
Note that OpenSSL uses the
.Nm
device for seeding automatically.
.Pp
Randomness for simulation is required in engineering or
scientific software and games.
The first requirement of these applications is
that the random numbers produced conform to some well-known,
usually uniform, distribution.
The sequence of numbers should also appear numerically uncorrelated,
as simulation often assumes independence of its random inputs.
Often it is desirable to reproduce
the results of a simulation exactly,
so that if the generator is seeded in the same way,
it should produce the same results.
A peripheral concern for simulation is
the speed of a random number generator.
.Pp
Another issue in simulation is
the size of the state associated with the random number generator, and
how frequently it repeats itself.
For example,
a program which shuffles a pack of cards should have 52!\& possible outputs,
which requires the random number generator to have 52!\& starting states.
This means the seed should have at least log_2(52!) ~ 226 bits of state
if the program is to stand a chance of outputting all possible sequences,
and the program needs some unbiased way of generating these bits.
Again,
the
.Nm
device could be used for seeding here,
but in practice, smaller seeds are usually considered acceptable.
.Pp
.Fx
provides two families of functions which are considered
suitable for simulation.
The
.Xr random 3
family of functions provides a random integer
between 0 to
.if t 2\u\s731\s10\d\(mi1.
.if n (2**31)\(mi1.
The functions
.Xr srandom 3 ,
.Xr initstate 3
and
.Xr setstate 3
are provided for deterministically setting
the state of the generator and
the function
.Xr srandomdev 3
is provided for setting the state via the
.Nm
device.
The
.Xr drand48 3
family of functions are also provided,
which provide random floating point numbers in various ranges.
.Pp
Randomness that is used for collision avoidance
(for example, in certain network protocols)
has slightly different semantics again.
It is usually expected that the numbers will be uniform,
as this produces the lowest chances of collision.
Here again,
the seeding of the generator is very important,
as it is required that different instances of
the generator produce independent sequences.
However, the guessability or reproducibility of the sequence is unimportant,
unlike the previous cases.
.Pp
.Fx
does also provide the traditional
.Xr rand 3
library call,
for compatibility purposes.
However,
it is known to be poor for simulation and
absolutely unsuitable for cryptographic purposes,
so its use is discouraged.
.Sh FILES .Sh FILES
.Bl -tag -width ".Pa /dev/random" .Bl -tag -width ".Pa /dev/urandom"
.It Pa /dev/random .It Pa /dev/random
.It Pa /dev/urandom
.El .El
.Sh SEE ALSO .Sh SEE ALSO
.Xr getrandom 2 ,
.Xr arc4random 3 , .Xr arc4random 3 ,
.Xr drand48 3 , .Xr getentropy 3 ,
.Xr rand 3 ,
.Xr RAND_add 3 ,
.Xr RAND_bytes 3 ,
.Xr random 3 , .Xr random 3 ,
.Xr sysctl 8 , .Xr sysctl 8 ,
.Xr random 9 .Xr random 9
@ -323,21 +153,86 @@ A
.Nm .Nm
device appeared in device appeared in
.Fx 2.2 . .Fx 2.2 .
The current software implementation, The implementation was changed to the
introduced in .Em Yarrow algorithm in
.Fx 10.0 ,
is by
.An Mark R V Murray ,
and is an implementation of the
.Em Fortuna
algorithm by Ferguson
.Em et al .
It replaces the previous
.Em Yarrow
implementation,
introduced in
.Fx 5.0 . .Fx 5.0 .
The Yarrow algorithm In
is no longer supported .Fx 11.0 ,
by its authors, the Fortuna algorithm was introduced as the default.
and is therefore no longer available. In
.Fx 12.0 ,
Yarrow was removed entirely.
.Sh AUTHORS
.An -nosplit
The current
.Nm
code was authored by
.An Mark R V Murray ,
with significant contributions from many people.
.Pp
The
.Em Fortuna
algorithm was designed by
.An Niels Ferguson ,
.An Bruce Schneier ,
and
.An Tadayoshi Kohno .
.Sh CAVEATS
When
.Cd "options RANDOM_LOADABLE"
is enabled,
the
.Pa /dev/random
device is not created
until an "algorithm module"
is loaded.
The only module built by default is
.Em random_fortuna .
Loadable random modules
are less efficient
than their compiled-in equivalents.
This is because some functions
must be locked against
load and unload events,
and also must be indirect calls
to allow for removal.
.Pp
When
.Cd "options RANDOM_ENABLE_UMA"
is enabled,
the
.Pa /dev/random
device will obtain entropy
from the zone allocator.
This is a very high rate source with significant performance impact.
Therefore, it is disabled by default.
.Pp
When
.Cd "options RANDOM_ENABLE_ETHER"
is enabled, the
.Nm
device will obtain entropy from
.Vt mbuf
structures passing through the network stack.
This source is both extremely expensive and a poor source of entropy, so it is
disabled by default.
.Sh SECURITY CONSIDERATIONS
The initial seeding
of random number generators
is a bootstrapping problem
that needs very careful attention.
When writable media is available, the
.Em Fortuna
paper describes a robust system for rapidly reseeding the device.
.Pp
In some embedded cases, it may be difficult to find enough randomness to seed a
random number generator until a system is fully operational.
In these cases, is the responsibility of the system architect to ensure that
blocking is acceptable, or that the random device is seeded.
(This advice does not apply to typical consumer systems.)
.Pp
To emulate embedded systems, developers may set the
.Va kern.random.block_seeded_status
tunable to 1 to verify boot does not require early availability of the
.Nm
device.

View File

@ -26,12 +26,13 @@
.\" .\"
.\" $FreeBSD$ .\" $FreeBSD$
.\" " .\" "
.Dd July 16, 2015 .Dd April 15, 2019
.Dt RANDOM 9 .Dt RANDOM 9
.Os .Os
.Sh NAME .Sh NAME
.Nm arc4rand , .Nm arc4rand ,
.Nm arc4random , .Nm arc4random ,
.Nm arc4random_buf ,
.Nm random , .Nm random ,
.Nm read_random , .Nm read_random ,
.Nm read_random_uio , .Nm read_random_uio ,
@ -39,86 +40,72 @@
.Nd supply pseudo-random numbers .Nd supply pseudo-random numbers
.Sh SYNOPSIS .Sh SYNOPSIS
.In sys/libkern.h .In sys/libkern.h
.Ft uint32_t
.Fn arc4random "void"
.Ft void
.Fn arc4random_buf "void *ptr" "size_t len"
.Ft void
.Fn arc4rand "void *ptr" "u_int length" "int reseed"
.Pp
.In sys/random.h
.Ft void
.Fn read_random "void *buffer" "int count"
.Ft int
.Fn read_random_uio "struct uio *uio" "bool nonblock"
.Ss LEGACY ROUTINES
.In sys/libkern.h
.Ft void .Ft void
.Fn srandom "u_long seed" .Fn srandom "u_long seed"
.Ft u_long .Ft u_long
.Fn random "void" .Fn random "void"
.Ft void
.Fn arc4rand "void *ptr" "u_int length" "int reseed"
.Ft uint32_t
.Fn arc4random "void"
.Pp
.In sys/random.h
.Ft int
.Fn read_random "void *buffer" "int count"
.Ft int
.Fn read_random_uio "struct uio *uio" "bool nonblock"
.Sh DESCRIPTION .Sh DESCRIPTION
The The
.Fn random .Fn arc4random
function will by default produce and
a sequence of numbers .Fn arc4random_buf
that can be duplicated functions will return very good quality random numbers, suited for
by calling security-related purposes.
.Fn srandom Both are wrappers around the underlying
with some constant .Fn arc4rand
as the interface.
.Fa seed . .Fn arc4random
The returns a 32-bit random value, while
.Fn srandom .Fn arc4random_buf
function may be called with any arbitrary fills
.Fa seed .Fa ptr
value to get slightly more unpredictable numbers. with
It is important to remember that the .Fa len
.Fn random bytes of random data.
function is entirely predictable,
and is therefore not of use where
knowledge of the sequence of numbers
may be of benefit to an attacker.
.Pp .Pp
The The
.Fn arc4rand .Fn arc4rand
function will return very good quality random numbers, CSPRNG
better suited is seeded from the
for security-related purposes. .Xr random 4
The random numbers from kernel abstract entropy device.
.Fn arc4rand Automatic reseeding happens at unspecified time and bytes (of output)
are seeded from the entropy device intervals.
if it is available. A reseed can be forced by passing a non-zero
Automatic reseeds happen
after a certain timeinterval
and after a certain number of bytes
have been delivered.
A forced reseed
can be forced
by passing a non-zero
value in the
.Fa reseed .Fa reseed
argument. value.
.Pp .Pp
The The
.Fn read_random .Fn read_random
function is used to return entropy directly from the entropy device function is used to read entropy directly from the kernel abstract entropy
if it has been loaded. device.
If the entropy device is not loaded, then .Fn read_random
the blocks if and until the entropy device is seeded.
.Fa buffer The provided
is ignored
and zero is returned.
The
.Fa buffer .Fa buffer
is filled with no more than is filled with no more than
.Fa count .Fa count
bytes. bytes.
It is strongly advised that It is strongly advised that
.Fn read_random .Fn read_random
is not used; is not used directly;
instead use instead, use the
.Fn arc4rand .Fn arc4rand
unless it is family of functions.
necessary to know
that no entropy
has been returned.
.Pp .Pp
The The
.Fn read_random_uio .Fn read_random_uio
@ -129,50 +116,35 @@ on
The The
.Fa uio .Fa uio
argument points to a buffer where random data should be stored. argument points to a buffer where random data should be stored.
This function only returns data if the random device is seeded. If
It blocks if unseeded,
except when the
.Fa nonblock .Fa nonblock
argument is true. is true and the random device is not seeded, this function does not return any
data.
Otherwise, this function may block interruptibly until the random device is seeded.
If the function is interrupted before the random device is seeded, no data is
returned.
.Pp .Pp
All the bits returned by The legacy
.Fn random , .Fn random
.Fn arc4rand , function will produce a sequence of numbers that can be duplicated by calling
.Fn read_random , .Fn srandom
and with some constant as the
.Fn read_random_uio .Fa seed .
are usable. The legacy
For example, .Fn srandom
.Sq Li random()&01 function may be called with any
will produce a random binary value. .Fa seed
.Pp value.
The It is strongly advised that the
.Fn arc4random .Fn random
is a convenience function which calls function not be used to generate random numbers.
.Fn arc4rand See
to return a 32 bit pseudo-random integer. .Sx SECURITY CONSIDERATIONS .
.Sh RETURN VALUES .Sh RETURN VALUES
The The
.Fn random
function uses
a non-linear additive feedback random number generator
employing a default table
of size 31
containing long integers
to return successive pseudo-random
numbers in the range from 0 to
.if t 2\u\s731\s10\d\(mi1.
.if n (2**31)\(mi1.
The period of this random number generator
is very large,
approximately
.if t 16\(mu(2\u\s731\s10\d\(mi1).
.if n 16*((2**31)\(mi1).
.Pp
The
.Fn arc4rand .Fn arc4rand
function uses the RC4 algorithm function uses the Chacha20 algorithm to generate a pseudo-random sequence of
to generate successive pseudo-random bytes. bytes.
The The
.Fn arc4random .Fn arc4random
function uses function uses
@ -191,6 +163,23 @@ the number of bytes placed in
.Fn read_random_uio .Fn read_random_uio
returns zero when successful, returns zero when successful,
otherwise an error code is returned. otherwise an error code is returned.
.Pp
The legacy
.Fn random
function uses
a non-linear additive feedback random number generator
employing a default table
of size 31
containing long integers
to return successive pseudo-random
numbers in the range from 0 to
.if t 2\u\s731\s10\d\(mi1.
.if n (2**31)\(mi1.
The period of this random number generator
is very large,
approximately
.if t 16\(mu(2\u\s731\s10\d\(mi1).
.if n 16*((2**31)\(mi1).
.Sh ERRORS .Sh ERRORS
.Fn read_random_uio .Fn read_random_uio
may fail if: may fail if:
@ -210,3 +199,19 @@ wrote
.An Mark R V Murray .An Mark R V Murray
wrote wrote
.Fn read_random . .Fn read_random .
.Sh SECURITY CONSIDERATIONS
Do not use
.Fn random
or
.Fn srandom
in new code.
.Pp
It is important to remember that the
.Fn random
function is entirely predictable.
It is easy for attackers to predict future output of
.Fn random
by recording some generated values.
We cannot emphasize strongly enough that
.Fn random
must not be used to generate values that are intended to be unpredictable.

View File

@ -451,16 +451,22 @@ random_fortuna_read(uint8_t *buf, u_int bytecount)
RANDOM_RESEED_UNLOCK(); RANDOM_RESEED_UNLOCK();
} }
#ifdef _KERNEL
static bool block_seeded_status = false;
SYSCTL_BOOL(_kern_random, OID_AUTO, block_seeded_status, CTLFLAG_RWTUN,
&block_seeded_status, 0,
"If non-zero, pretend Fortuna is in an unseeded state. By setting "
"this as a tunable, boot can be tested as if the random device is "
"unavailable.");
#endif
bool bool
random_fortuna_seeded(void) random_fortuna_seeded(void)
{ {
#ifdef _KERNEL #ifdef _KERNEL
/* When set, act as if we are not seeded. */ if (block_seeded_status)
KFAIL_POINT_CODE(DEBUG_FP, random_fortuna_seeded, { return (false);
if (RETURN_VALUE != 0)
fortuna_state.fs_counter = UINT128_ZERO;
});
#endif #endif
return (!uint128_is_zero(fortuna_state.fs_counter)); return (!uint128_is_zero(fortuna_state.fs_counter));

View File

@ -421,11 +421,6 @@ random_harvestq_prime(void *unused __unused)
if (keyfile != NULL) { if (keyfile != NULL) {
data = preload_fetch_addr(keyfile); data = preload_fetch_addr(keyfile);
size = preload_fetch_size(keyfile); size = preload_fetch_size(keyfile);
/* skip the first bit of the stash so others like arc4 can also have some. */
if (size > RANDOM_CACHED_SKIP_START) {
data += RANDOM_CACHED_SKIP_START;
size -= RANDOM_CACHED_SKIP_START;
}
/* Trim the size. If the admin has a file with a funny size, we lose some. Tough. */ /* Trim the size. If the admin has a file with a funny size, we lose some. Tough. */
size -= (size % sizeof(event.he_entropy)); size -= (size % sizeof(event.he_entropy));
if (data != NULL && size != 0) { if (data != NULL && size != 0) {

View File

@ -57,12 +57,18 @@ struct random_algorithm *p_random_alg_context = &random_alg_context;
#if defined(RANDOM_LOADABLE) #if defined(RANDOM_LOADABLE)
static void
null_read_random(void *dummy __unused, u_int dummy2 __unused)
{
panic("%s: no random module is loaded", __func__);
}
struct random_readers { struct random_readers {
int (*read_random_uio)(struct uio *, bool); int (*read_random_uio)(struct uio *, bool);
u_int (*read_random)(void *, u_int); void (*read_random)(void *, u_int);
} random_reader_context = { } random_reader_context = {
(int (*)(struct uio *, bool))nullop, (int (*)(struct uio *, bool))nullop,
(u_int (*)(void *, u_int))nullop, null_read_random,
}; };
struct sx randomdev_config_lock; struct sx randomdev_config_lock;
@ -76,7 +82,7 @@ random_infra_sysinit(void *dummy __unused)
SYSINIT(random_device_h_init, SI_SUB_RANDOM, SI_ORDER_FIRST, random_infra_sysinit, NULL); SYSINIT(random_device_h_init, SI_SUB_RANDOM, SI_ORDER_FIRST, random_infra_sysinit, NULL);
void void
random_infra_init(int (*p_random_read_uio)(struct uio *, bool), u_int (*p_random_read)(void *, u_int)) random_infra_init(int (*p_random_read_uio)(struct uio *, bool), void (*p_random_read)(void *, u_int))
{ {
RANDOM_CONFIG_X_LOCK(); RANDOM_CONFIG_X_LOCK();
@ -91,7 +97,7 @@ random_infra_uninit(void)
RANDOM_CONFIG_X_LOCK(); RANDOM_CONFIG_X_LOCK();
random_reader_context.read_random_uio = (int (*)(struct uio *, bool))nullop; random_reader_context.read_random_uio = (int (*)(struct uio *, bool))nullop;
random_reader_context.read_random = (u_int (*)(void *, u_int))nullop; random_reader_context.read_random = null_read_random;
RANDOM_CONFIG_X_UNLOCK(); RANDOM_CONFIG_X_UNLOCK();
} }
@ -114,15 +120,13 @@ read_random_uio(struct uio *uio, bool nonblock)
return (retval); return (retval);
} }
u_int void
read_random(void *buf, u_int len) read_random(void *buf, u_int len)
{ {
u_int retval;
RANDOM_CONFIG_S_LOCK(); RANDOM_CONFIG_S_LOCK();
retval = random_reader_context.read_random(buf, len); random_reader_context.read_random(buf, len);
RANDOM_CONFIG_S_UNLOCK(); RANDOM_CONFIG_S_UNLOCK();
return (retval);
} }
#endif /* defined(RANDOM_LOADABLE) */ #endif /* defined(RANDOM_LOADABLE) */

View File

@ -63,7 +63,7 @@ __FBSDID("$FreeBSD$");
#define READ_RANDOM_UIO _read_random_uio #define READ_RANDOM_UIO _read_random_uio
#define READ_RANDOM _read_random #define READ_RANDOM _read_random
static int READ_RANDOM_UIO(struct uio *, bool); static int READ_RANDOM_UIO(struct uio *, bool);
static u_int READ_RANDOM(void *, u_int); static void READ_RANDOM(void *, u_int);
#else #else
#define READ_RANDOM_UIO read_random_uio #define READ_RANDOM_UIO read_random_uio
#define READ_RANDOM read_random #define READ_RANDOM read_random
@ -124,11 +124,53 @@ randomdev_read(struct cdev *dev __unused, struct uio *uio, int flags)
return (READ_RANDOM_UIO(uio, (flags & O_NONBLOCK) != 0)); return (READ_RANDOM_UIO(uio, (flags & O_NONBLOCK) != 0));
} }
/*
* If the random device is not seeded, blocks until it is seeded.
*
* Returns zero when the random device is seeded.
*
* If the 'interruptible' parameter is true, and the device is unseeded, this
* routine may be interrupted. If interrupted, it will return either ERESTART
* or EINTR.
*/
#define SEEDWAIT_INTERRUPTIBLE true
#define SEEDWAIT_UNINTERRUPTIBLE false
static int
randomdev_wait_until_seeded(bool interruptible)
{
int error, spamcount, slpflags;
slpflags = interruptible ? PCATCH : 0;
error = 0;
spamcount = 0;
while (!p_random_alg_context->ra_seeded()) {
/* keep tapping away at the pre-read until we seed/unblock. */
p_random_alg_context->ra_pre_read();
/* Only bother the console every 10 seconds or so */
if (spamcount == 0)
printf("random: %s unblock wait\n", __func__);
spamcount = (spamcount + 1) % 100;
error = tsleep(&random_alg_context, slpflags, "randseed",
hz / 10);
if (error == ERESTART || error == EINTR) {
KASSERT(interruptible,
("unexpected wake of non-interruptible sleep"));
break;
}
/* Squash tsleep timeout condition */
if (error == EWOULDBLOCK)
error = 0;
KASSERT(error == 0, ("unexpected tsleep error %d", error));
}
return (error);
}
int int
READ_RANDOM_UIO(struct uio *uio, bool nonblock) READ_RANDOM_UIO(struct uio *uio, bool nonblock)
{ {
uint8_t *random_buf; uint8_t *random_buf;
int error, spamcount; int error;
ssize_t read_len, total_read, c; ssize_t read_len, total_read, c;
/* 16 MiB takes about 0.08 s CPU time on my 2017 AMD Zen CPU */ /* 16 MiB takes about 0.08 s CPU time on my 2017 AMD Zen CPU */
#define SIGCHK_PERIOD (16 * 1024 * 1024) #define SIGCHK_PERIOD (16 * 1024 * 1024)
@ -140,26 +182,13 @@ READ_RANDOM_UIO(struct uio *uio, bool nonblock)
random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK);
p_random_alg_context->ra_pre_read(); p_random_alg_context->ra_pre_read();
error = 0; error = 0;
spamcount = 0;
/* (Un)Blocking logic */ /* (Un)Blocking logic */
while (!p_random_alg_context->ra_seeded()) { if (!p_random_alg_context->ra_seeded()) {
if (nonblock) { if (nonblock)
error = EWOULDBLOCK; error = EWOULDBLOCK;
break; else
} error = randomdev_wait_until_seeded(
/* keep tapping away at the pre-read until we seed/unblock. */ SEEDWAIT_INTERRUPTIBLE);
p_random_alg_context->ra_pre_read();
/* Only bother the console every 10 seconds or so */
if (spamcount == 0)
printf("random: %s unblock wait\n", __func__);
spamcount = (spamcount + 1)%100;
error = tsleep(&random_alg_context, PCATCH, "randseed", hz/10);
if (error == ERESTART || error == EINTR)
break;
/* Squash tsleep timeout condition */
if (error == EWOULDBLOCK)
error = 0;
KASSERT(error == 0, ("unexpected tsleep error %d", error));
} }
if (error == 0) { if (error == 0) {
read_rate_increment((uio->uio_resid + sizeof(uint32_t))/sizeof(uint32_t)); read_rate_increment((uio->uio_resid + sizeof(uint32_t))/sizeof(uint32_t));
@ -210,30 +239,36 @@ READ_RANDOM_UIO(struct uio *uio, bool nonblock)
* It cannot assumed that random_buf is a multiple of * It cannot assumed that random_buf is a multiple of
* RANDOM_BLOCKSIZE bytes. * RANDOM_BLOCKSIZE bytes.
*/ */
u_int void
READ_RANDOM(void *random_buf, u_int len) READ_RANDOM(void *random_buf, u_int len)
{ {
u_int read_len; u_int read_directly_len;
uint8_t local_buf[len + RANDOM_BLOCKSIZE];
KASSERT(random_buf != NULL, ("No suitable random buffer in %s", __func__)); KASSERT(random_buf != NULL, ("No suitable random buffer in %s", __func__));
p_random_alg_context->ra_pre_read(); p_random_alg_context->ra_pre_read();
/* (Un)Blocking logic; if not seeded, return nothing. */ /* (Un)Blocking logic */
if (p_random_alg_context->ra_seeded()) { if (!p_random_alg_context->ra_seeded())
read_rate_increment((len + sizeof(uint32_t))/sizeof(uint32_t)); (void)randomdev_wait_until_seeded(SEEDWAIT_UNINTERRUPTIBLE);
if (len > 0) { read_rate_increment(roundup2(len, sizeof(uint32_t)));
/* if (len == 0)
* Belt-and-braces. return;
* Round up the read length to a crypto block size multiple, /*
* which is what the underlying generator is expecting. * The underlying generator expects multiples of
*/ * RANDOM_BLOCKSIZE.
read_len = roundup(len, RANDOM_BLOCKSIZE); */
p_random_alg_context->ra_read(local_buf, read_len); read_directly_len = rounddown(len, RANDOM_BLOCKSIZE);
memcpy(random_buf, local_buf, len); if (read_directly_len > 0)
} p_random_alg_context->ra_read(random_buf, read_directly_len);
} else if (read_directly_len < len) {
len = 0; uint8_t remainder_buf[RANDOM_BLOCKSIZE];
return (len);
p_random_alg_context->ra_read(remainder_buf,
sizeof(remainder_buf));
memcpy((char *)random_buf + read_directly_len, remainder_buf,
len - read_directly_len);
explicit_bzero(remainder_buf, sizeof(remainder_buf));
}
} }
static __inline void static __inline void

View File

@ -118,7 +118,7 @@ extern struct sx randomdev_config_lock;
#define RANDOM_CONFIG_S_LOCK(x) sx_slock(&randomdev_config_lock) #define RANDOM_CONFIG_S_LOCK(x) sx_slock(&randomdev_config_lock)
#define RANDOM_CONFIG_S_UNLOCK(x) sx_sunlock(&randomdev_config_lock) #define RANDOM_CONFIG_S_UNLOCK(x) sx_sunlock(&randomdev_config_lock)
#define RANDOM_CONFIG_DEINIT_LOCK(x) sx_destroy(&randomdev_config_lock) #define RANDOM_CONFIG_DEINIT_LOCK(x) sx_destroy(&randomdev_config_lock)
void random_infra_init(int (*)(struct uio *, bool), u_int (*)(void *, u_int)); void random_infra_init(int (*)(struct uio *, bool), void (*)(void *, u_int));
void random_infra_uninit(void); void random_infra_uninit(void);
#endif #endif

View File

@ -159,15 +159,8 @@ sysctl_kern_arnd(SYSCTL_HANDLER_ARGS)
char buf[256]; char buf[256];
size_t len; size_t len;
/*- len = MIN(req->oldlen, sizeof(buf));
* This is one of the very few legitimate uses of read_random(9). read_random(buf, len);
* Use of arc4random(9) is not recommended as that will ignore
* an unsafe (i.e. unseeded) random(4).
*
* If random(4) is not seeded, then this returns 0, so the
* sysctl will return a zero-length buffer.
*/
len = read_random(buf, MIN(req->oldlen, sizeof(buf)));
return (SYSCTL_OUT(req, buf, len)); return (SYSCTL_OUT(req, buf, len));
} }

View File

@ -56,7 +56,6 @@ MALLOC_DEFINE(M_CHACHA20RANDOM, "chacha20random", "chacha20random structures");
struct chacha20_s { struct chacha20_s {
struct mtx mtx; struct mtx mtx;
int numbytes; int numbytes;
int first_time_done;
time_t t_reseed; time_t t_reseed;
u_int8_t m_buffer[CHACHA20_BUFFER_SIZE]; u_int8_t m_buffer[CHACHA20_BUFFER_SIZE];
struct chacha_ctx ctx; struct chacha_ctx ctx;
@ -73,35 +72,17 @@ static struct chacha20_s *chacha20inst = NULL;
* Mix up the current context. * Mix up the current context.
*/ */
static void static void
chacha20_randomstir(struct chacha20_s* chacha20) chacha20_randomstir(struct chacha20_s *chacha20)
{ {
struct timeval tv_now; struct timeval tv_now;
size_t n, size; u_int8_t key[CHACHA20_KEYBYTES];
u_int8_t key[CHACHA20_KEYBYTES], *data;
caddr_t keyfile;
/* /*
* This is making the best of what may be an insecure * If the loader(8) did not have an entropy stash from the previous
* Situation. If the loader(8) did not have an entropy * shutdown to load, then we will block. The answer is to make sure
* stash from the previous shutdown to load, then we will * there is an entropy stash at shutdown time.
* be improperly seeded. The answer is to make sure there
* is an entropy stash at shutdown time.
*/ */
(void)read_random(key, CHACHA20_KEYBYTES); read_random(key, CHACHA20_KEYBYTES);
if (!chacha20->first_time_done) {
keyfile = preload_search_by_type(RANDOM_CACHED_BOOT_ENTROPY_MODULE);
if (keyfile != NULL) {
data = preload_fetch_addr(keyfile);
size = MIN(preload_fetch_size(keyfile), CHACHA20_KEYBYTES);
for (n = 0; n < size; n++)
key[n] ^= data[n];
explicit_bzero(data, size);
if (bootverbose)
printf("arc4random: read %zu bytes from preloaded cache\n", size);
} else
printf("arc4random: no preloaded entropy cache\n");
chacha20->first_time_done = 1;
}
getmicrouptime(&tv_now); getmicrouptime(&tv_now);
mtx_lock(&chacha20->mtx); mtx_lock(&chacha20->mtx);
chacha_keysetup(&chacha20->ctx, key, CHACHA20_KEYBYTES*8); chacha_keysetup(&chacha20->ctx, key, CHACHA20_KEYBYTES*8);
@ -128,7 +109,6 @@ chacha20_init(void)
mtx_init(&chacha20->mtx, "chacha20_mtx", NULL, MTX_DEF); mtx_init(&chacha20->mtx, "chacha20_mtx", NULL, MTX_DEF);
chacha20->t_reseed = -1; chacha20->t_reseed = -1;
chacha20->numbytes = 0; chacha20->numbytes = 0;
chacha20->first_time_done = 0;
explicit_bzero(chacha20->m_buffer, CHACHA20_BUFFER_SIZE); explicit_bzero(chacha20->m_buffer, CHACHA20_BUFFER_SIZE);
explicit_bzero(&chacha20->ctx, sizeof(chacha20->ctx)); explicit_bzero(&chacha20->ctx, sizeof(chacha20->ctx));
} }

View File

@ -38,7 +38,7 @@
struct uio; struct uio;
#if defined(DEV_RANDOM) #if defined(DEV_RANDOM)
u_int read_random(void *, u_int); void read_random(void *, u_int);
int read_random_uio(struct uio *, bool); int read_random_uio(struct uio *, bool);
#else #else
static __inline int static __inline int
@ -46,10 +46,9 @@ read_random_uio(void *a __unused, u_int b __unused)
{ {
return (0); return (0);
} }
static __inline u_int static __inline void
read_random(void *a __unused, u_int b __unused) read_random(void *a __unused, u_int b __unused)
{ {
return (0);
} }
#endif #endif
@ -95,7 +94,6 @@ _Static_assert(ENTROPYSOURCE <= 32,
#define RANDOM_LEGACY_BOOT_ENTROPY_MODULE "/boot/entropy" #define RANDOM_LEGACY_BOOT_ENTROPY_MODULE "/boot/entropy"
#define RANDOM_CACHED_BOOT_ENTROPY_MODULE "boot_entropy_cache" #define RANDOM_CACHED_BOOT_ENTROPY_MODULE "boot_entropy_cache"
#define RANDOM_CACHED_SKIP_START 256
#if defined(DEV_RANDOM) #if defined(DEV_RANDOM)
extern u_int hc_source_mask; extern u_int hc_source_mask;