Huge cleanup of random(4) code.

* GENERAL
- Update copyright.
- Make kernel options for RANDOM_YARROW and RANDOM_DUMMY. Set
  neither to ON, which means we want Fortuna
- If there is no 'device random' in the kernel, there will be NO
  random(4) device in the kernel, and the KERN_ARND sysctl will
  return nothing. With RANDOM_DUMMY there will be a random(4) that
  always blocks.
- Repair kern.arandom (KERN_ARND sysctl). The old version went
  through arc4random(9) and was a bit weird.
- Adjust arc4random stirring a bit - the existing code looks a little
  suspect.
- Fix the nasty pre- and post-read overloading by providing explictit
  functions to do these tasks.
- Redo read_random(9) so as to duplicate random(4)'s read internals.
  This makes it a first-class citizen rather than a hack.
- Move stuff out of locked regions when it does not need to be
  there.
- Trim RANDOM_DEBUG printfs. Some are excess to requirement, some
  behind boot verbose.
- Use SYSINIT to sequence the startup.
- Fix init/deinit sysctl stuff.
- Make relevant sysctls also tunables.
- Add different harvesting "styles" to allow for different requirements
  (direct, queue, fast).
- Add harvesting of FFS atime events. This needs to be checked for
  weighing down the FS code.
- Add harvesting of slab allocator events. This needs to be checked for
  weighing down the allocator code.
- Fix the random(9) manpage.
- Loadable modules are not present for now. These will be re-engineered
  when the dust settles.
- Use macros for locks.
- Fix comments.

* src/share/man/...
- Update the man pages.

* src/etc/...
- The startup/shutdown work is done in D2924.

* src/UPDATING
- Add UPDATING announcement.

* src/sys/dev/random/build.sh
- Add copyright.
- Add libz for unit tests.

* src/sys/dev/random/dummy.c
- Remove; no longer needed. Functionality incorporated into randomdev.*.

* live_entropy_sources.c live_entropy_sources.h
- Remove; content moved.
- move content to randomdev.[ch] and optimise.

* src/sys/dev/random/random_adaptors.c src/sys/dev/random/random_adaptors.h
- Remove; plugability is no longer used. Compile-time algorithm
  selection is the way to go.

* src/sys/dev/random/random_harvestq.c src/sys/dev/random/random_harvestq.h
- Add early (re)boot-time randomness caching.

* src/sys/dev/random/randomdev_soft.c src/sys/dev/random/randomdev_soft.h
- Remove; no longer needed.

* src/sys/dev/random/uint128.h
- Provide a fake uint128_t; if a real one ever arrived, we can use
  that instead. All that is needed here is N=0, N++, N==0, and some
  localised trickery is used to manufacture a 128-bit 0ULLL.

* src/sys/dev/random/unit_test.c src/sys/dev/random/unit_test.h
- Improve unit tests; previously the testing human needed clairvoyance;
  now the test will do a basic check of compressibility. Clairvoyant
  talent is still a good idea.
- This is still a long way off a proper unit test.

* src/sys/dev/random/fortuna.c src/sys/dev/random/fortuna.h
- Improve messy union to just uint128_t.
- Remove unneeded 'static struct fortuna_start_cache'.
- Tighten up up arithmetic.
- Provide a method to allow eternal junk to be introduced; harden
  it against blatant by compress/hashing.
- Assert that locks are held correctly.
- Fix the nasty pre- and post-read overloading by providing explictit
  functions to do these tasks.
- Turn into self-sufficient module (no longer requires randomdev_soft.[ch])

* src/sys/dev/random/yarrow.c src/sys/dev/random/yarrow.h
- Improve messy union to just uint128_t.
- Remove unneeded 'staic struct start_cache'.
- Tighten up up arithmetic.
- Provide a method to allow eternal junk to be introduced; harden
  it against blatant by compress/hashing.
- Assert that locks are held correctly.
- Fix the nasty pre- and post-read overloading by providing explictit
  functions to do these tasks.
- Turn into self-sufficient module (no longer requires randomdev_soft.[ch])
- Fix some magic numbers elsewhere used as FAST and SLOW.

Differential Revision: https://reviews.freebsd.org/D2025
Reviewed by: vsevolod,delphij,rwatson,trasz,jmg
Approved by: so (delphij)
This commit is contained in:
Mark Murray 2015-06-30 17:00:45 +00:00
parent 6ef120027f
commit d1b06863fb
64 changed files with 1810 additions and 2492 deletions

View File

@ -31,6 +31,41 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 11.x IS SLOW:
disable the most expensive debugging functionality run
"ln -s 'abort:false,junk:false' /etc/malloc.conf".)
20150630:
The default kernel entropy-processing algorithm is now
Fortuna, replacing Yarrow.
Assuming you have 'device random' in your kernel config
file, the configurations allow a kernel option to override
this default. You may choose *ONE* of:
options RANDOM_YARROW # Legacy /dev/random algorithm.
options RANDOM_DUMMY # Blocking-only driver.
If you have neither, you get Fortuna. For most people,
read no further, Fortuna will give a /dev/random that works
like it always used to, and the difference will be irrelevant.
If you remove 'device random', you get *NO* kernel-processed
entopy at all. This may be acceptable to folks building
embedded systems, but has complications. Carry on reading,
and it is assumed you know what you need.
*PLEASE* read random(4) and random(9) if you are in the
habit of tweeking kernel configs, and/or if you are a member
of the embedded community, wanting specific and not-usual
behaviour from your security subsystems.
NOTE!! If you use RANDOM_DUMMY and/or have no 'device
random', you will NOT have a functioning /dev/random, and
many cryptographic features will not work, including SSH.
You may also find strange behaviour from the random(3) set
of library functions, in particular sranddev(3), srandomdev(3)
and arc4random(3). The reason for this is that the KERN_ARND
sysctl only returns entropy if it thinks it has some to
share, and with RANDOM_DUMMY or no 'device random' this
will never happen.
20150623:
An additional fix for the issue described in the 20150614 sendmail
entry below has been been committed in revision 284717.

View File

@ -1,4 +1,4 @@
.\" Copyright (c) 2001-2013 Mark R V Murray. All rights reserved.
.\" Copyright (c) 2001-2015 Mark R V Murray. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
@ -23,7 +23,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd October 12, 2013
.Dd June 30, 2015
.Dt RANDOM 4
.Os
.Sh NAME
@ -37,31 +37,32 @@ The
device
returns an endless supply of random bytes when read.
It also accepts and reads data
as any ordinary (and willing) file,
but discards data written to it.
The device will probe for
certain hardware entropy sources,
and use these in preference to the fallback,
which is a generator implemented in software.
as any ordinary file.
.Pp
The software generator will start in an
The generator will start in an
.Em unseeded
state, and will block reads until
it is (re)seeded.
it is seeded for the first time.
This may cause trouble at system boot
when keys and the like
are generated from
/dev/random
.Xr random 4
so steps should be taken to ensure a
reseed as soon as possible.
The
.Xr sysctl 8
controlling the
.Em seeded
status (see below) may be used
if security is not an issue
or for convenience
during setup or development.
seeding as soon as possible.
.Pp
It is also possible
to read random bytes
by using the KERN_ARND sysctl.
On the command line
this could be done by
.Pp
.Dl "sysctl -x -B 16 kern.arandom"
.Pp
This sysctl will not return
random bytes unless
the
.Xr random 4
is seeded.
.Pp
This initial seeding
of random number generators
@ -90,101 +91,57 @@ To see the current settings of the software
.Nm
device, use the command line:
.Pp
.Dl sysctl kern.random
.Dl "sysctl kern.random"
.Pp
which results in something like:
.Bd -literal -offset indent
kern.random.adaptors: yarrow,dummy
kern.random.active_adaptor: yarrow
kern.random.yarrow.gengateinterval: 10
kern.random.yarrow.bins: 10
kern.random.yarrow.fastthresh: 96
kern.random.yarrow.slowthresh: 128
kern.random.yarrow.slowoverthresh: 2
kern.random.sys.seeded: 1
kern.random.sys.harvest.ethernet: 1
kern.random.sys.harvest.point_to_point: 1
kern.random.sys.harvest.interrupt: 1
kern.random.sys.harvest.swi: 1
kern.random.fortuna.minpoolsize: 64
kern.random.harvest.mask_symbolic: [HIGH_PERFORMANCE], ... ,CACHED
kern.random.harvest.mask_bin: 00111111111
kern.random.harvest.mask: 511
kern.random.random_sources: 'Intel Secure Key RNG'
.Ed
.Pp
Other than
.Dl kern.random.adaptors
all settings are read/write.
.Dl kern.random.fortuna.minpoolsize
and
.Dl kern.random.harvest.mask
all settings are read-only.
.Pp
The
.Va kern.random.sys.seeded
variable indicates whether or not the
.Nm
device is in an acceptably secure state
as a result of reseeding.
If set to 0,
the device will block (on read)
until the next reseed
as a result of entropy harvesting.
A reseed will set the value to 1 (non-blocking).
.Pa kern.random.fortuna.minpoolsize
sysctl is used
to set the seed threshhold.
A smaller number gives a faster seed,
but a less secure one.
In practice,
values between 64 and 256
are acceptable.
.Pp
The
.Va kern.random.sys.harvest.ethernet
variable is used to select LAN traffic as an entropy source.
A 0 (zero) value means that LAN traffic
is not considered as an entropy source.
Set the variable to 1 (one)
if you wish to use LAN traffic for entropy harvesting.
.Pp
The
.Va kern.random.sys.harvest.point_to_point
variable is used to select serial line traffic as an entropy source.
(Serial line traffic includes PPP, SLIP and all tun0 traffic.)
A 0 (zero) value means such traffic
is not considered as an entropy source.
Set the variable to 1 (one)
if you wish to use it for entropy harvesting.
.Pp
The
.Va kern.random.sys.harvest.interrupt
variable is used to select hardware interrupts
.Va kern.random.harvest.mask
bitmask is used to select
the possible entropy sources.
A 0 (zero) value means
the corresponding source
is not considered
as an entropy source.
A 0 (zero) value means hardware interrupts
are not considered as an entropy source.
Set the variable to 1 (one)
if you wish to use them for entropy harvesting.
All hardware interrupt harvesting is set up by the
individual device drivers.
.Pp
Set the bit to 1 (one)
if you wish to use
that source.
The
.Va kern.random.sys.harvest.swi
variable is used to select software interrupts
as an entropy source.
A 0 (zero) value means software interrupts
are not considered as an entropy source.
Set the variable to 1 (one)
if you wish to use them for entropy harvesting.
.Pp
The other variables are explained in the paper describing the
.Em Yarrow
algorithm at
.Pa http://www.schneier.com/yarrow.html .
.Pp
These variables are all limited
in terms of the values they may contain:
.Bl -tag -width "kern.random.yarrow.gengateinterval" -compact -offset indent
.It Va kern.random.yarrow.gengateinterval
.Bq 4..64
.It Va kern.random.yarrow.bins
.Bq 2..16
.It Va kern.random.yarrow.fastthresh
.Bq 64..256
.It Va kern.random.yarrow.slowthresh
.Bq 64..256
.It Va kern.random.yarrow.slowoverthresh
.Bq 1..5
.El
.Pp
Internal
.Xr sysctl 3
handlers force the above variables
into the stated ranges.
.Va kern.random.harvest.mask_bin
and
.Va kern.random.harvest.mask_symbolic
sysctl
can be used confirm
that your choices are correct.
Note that disabled items
in the latter item
are listed in square brackets.
See
.Xr random_harvest 9
for more on the harvesting of entropy.
.Sh RANDOMNESS
The use of randomness in the field of computing
is a rather subtle issue because randomness means
@ -308,23 +265,36 @@ so its use is discouraged.
.Xr RAND_add 3 ,
.Xr RAND_bytes 3 ,
.Xr random 3 ,
.Xr sysctl 8
.Xr sysctl 8 ,
.Xr random 9
.Rs
.%A Ferguson
.%A Schneier
.%A Kohno
.%B Cryptography Engineering
.%I Wiley
.%O ISBN 978-0-470-47424-2
.Re
.Sh HISTORY
A
.Nm
device appeared in
.Fx 2.2 .
The early version was taken from Theodore Ts'o's entropy driver for Linux.
The current software implementation,
introduced in
.Fx 5.0 ,
is a complete rewrite by
.Fx 10.0 ,
is by
.An Mark R V Murray ,
and is an implementation of the
.Em Yarrow
algorithm by Bruce Schneier,
.Em Fortuna
algorithm by Ferguson
.Em et al .
Significant infrastructure work was done by Arthur Mesh.
.Pp
The author gratefully acknowledges
significant assistance from VIA Technologies, Inc.
It replaces the previous
.Em Yarrow
implementation,
introduced in
.Fx 5.0 .
The older
.Em Yarrow
algorithm remains available
as a compile-time fallback.

View File

@ -1,4 +1,6 @@
.\"
.\" Copyright (c) 2015
.\" Mark R V Murray
.\" Copyright (c) 2000
.\" The Regents of the University of California. All rights reserved.
.\"
@ -26,7 +28,7 @@
.\"
.\" $FreeBSD$
.\" "
.Dd September 25, 2000
.Dd June 30, 2015
.Dt RANDOM 9
.Os
.Sh NAME
@ -53,11 +55,12 @@
.Sh DESCRIPTION
The
.Fn random
function will by default produce a sequence of numbers that can be duplicated
function will by default produce
a sequence of numbers
that can be duplicated
by calling
.Fn srandom
with
.Ql 1
with some constant
as the
.Fa seed .
The
@ -67,19 +70,28 @@ function may be called with any arbitrary
value to get slightly more unpredictable numbers.
It is important to remember that the
.Fn random
function is entirely predictable, and is therefore not of use where
knowledge of the sequence of numbers may be of benefit to an attacker.
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
The
.Fn arc4rand
function will return very good quality random numbers, slightly better
suited for security-related purposes.
function will return very good quality random numbers,
better suited
for security-related purposes.
The random numbers from
.Fn arc4rand
are seeded from the entropy device if it is available.
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
are seeded from the entropy device
if it is available.
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
argument.
.Pp
@ -90,19 +102,24 @@ if it has been loaded.
If the entropy device is not loaded, then
the
.Fa buffer
is filled with output generated by
.Fn random .
is ignored
and zero is returned.
The
.Fa buffer
is filled with no more than
.Fa count
bytes.
It is advised that
It is strongly advised that
.Fn read_random
is not used; instead use
is not used;
instead use
.Fn arc4rand
unless it is
necessary to know
that no entropy
has been returned.
.Pp
All the bits generated by
All the bits returned by
.Fn random ,
.Fn arc4rand
and
@ -120,32 +137,38 @@ to return a 32 bit pseudo-random integer.
.Sh RETURN VALUES
The
.Fn random
function
uses a non-linear additive feedback random number generator employing a
default table of size 31 long integers to return successive pseudo-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
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
function uses the RC4 algorithm to generate successive pseudo-random
bytes.
function uses the RC4 algorithm
to generate successive pseudo-random bytes.
The
.Fn arc4random
function
uses
function uses
.Fn arc4rand
to generate pseudo-random numbers in the range from 0 to
to generate pseudo-random numbers
in the range from 0 to
.if t 2\u\s732\s10\d\(mi1.
.if n (2**32)\(mi1.
.Pp
The
.Fn read_random
function returns the number of bytes placed in
function returns
the number of bytes placed in
.Fa buffer .
.Sh AUTHORS
.An Dan Moschuk

View File

@ -1,5 +1,5 @@
.\"
.\" Copyright (c) 2002 Mark R V Murray
.\" Copyright (c) 2002-2015 Mark R V Murray
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd February 6, 2002
.Dd June 30, 2015
.Dt RANDOM_HARVEST 9
.Os
.Sh NAME
@ -35,59 +35,93 @@
.In sys/types.h
.In sys/random.h
.Ft void
.Fo random_harvest
.Fo random_harvest_direct
.Fa "void *entropy"
.Fa "u_int size"
.Fa "u_int bits"
.Fa "enum esource source"
.Fc
.Ft void
.Fo random_harvest_fast
.Fa "void *entropy"
.Fa "u_int size"
.Fa "u_int bits"
.Fa "enum esource source"
.Fc
.Ft void
.Fo random_harvest_queue
.Fa "void *entropy"
.Fa "u_int size"
.Fa "u_int bits"
.Fa "u_int frac"
.Fa "enum esource source"
.Fc
.Sh DESCRIPTION
The
.Fn random_harvest
function is used by device drivers
.Fn random_harvest_*
functions are used by device drivers
and other kernel processes to pass data
that is considered (at least partially) stochastic
to the entropy device.
.Pp
The caller should pass a pointer (to no more than 16 bytes) of
the
The caller should pass
a pointer pointing to the
.Dq random
data in
.Fa entropy .
The argument
.Fa size
contains the number of bytes pointed to.
The caller should
The
.Fa source
is chosen from one of
the values enumerated in
.Pa sys/dev/random.h .
and is used to indicate the source of the entropy.
.Pp
The
.Fo random_harvest_direct
.Fc
variant is used
for early harvesting
before any multitasking
is enabled.
.Pp
The
.Fn random_harvest_fast
variant is used
by sources that
should not take
a performance hit
from harvesting,
as they are high-rate
sources.
Some entropy is sacrificed,
but the hig rate of supply
will compensate for this.
.Pp
The
.Fn random_harvest_queue
variant is used
for general harvesting
and is the default
choice for most entropy sources
such as interrupts
or console events.
.Pp
The
.Fa bits
argument is only used
by the deprecated Yarrow algorithm.
For compatibility,
the caller should
.Em "very conservatively"
estimate the number of random bits
in the sample,
and pass this in
.Fa bits
or
.Fa frac .
If the estimated number of bits per sample is an integer, then
.Fa bits
is used, and
.Fa frac
is 0.
Otherwise,
for low-entropy samples,
.Dq fractional
entropy can be supplied in
.Fa frac .
(This is considered to be
.Fa frac /
1024 bits of entropy.)
The
.Fa source
is chosen from
.Dv RANDOM_WRITE , RANDOM_KEYBOARD , RANDOM_MOUSE , RANDOM_NET
and
.Dv RANDOM_INTERRUPT ,
and is used to indicate the source of the entropy.
.Fa bits .
.Pp
Interrupt harvesting has been simplified
Interrupt harvesting has been
in part simplified simplified
for the kernel programmer.
If a device driver registers an interrupt handler
with
@ -101,6 +135,7 @@ bit in the
.Fa flags
argument to have that interrupt source
be used for entropy harvesting.
This should be done wherever practicable.
.Sh SEE ALSO
.Xr random 4 ,
.Xr BUS_SETUP_INTR 9

View File

@ -528,14 +528,14 @@ crypto/des/des_ecb.c optional crypto | ipsec | netsmb
crypto/des/des_setkey.c optional crypto | ipsec | netsmb
crypto/rc4/rc4.c optional netgraph_mppc_encryption | kgssapi
crypto/rijndael/rijndael-alg-fst.c optional crypto | geom_bde | \
ipsec | random | wlan_ccmp
crypto/rijndael/rijndael-api-fst.c optional geom_bde | random
ipsec | random random_yarrow | random !random_yarrow !random_dummy | wlan_ccmp
crypto/rijndael/rijndael-api-fst.c optional geom_bde | random random_yarrow | random !random_yarrow !random_dummy
crypto/rijndael/rijndael-api.c optional crypto | ipsec | wlan_ccmp
crypto/sha1.c optional carp | crypto | ipsec | \
netgraph_mppc_encryption | sctp
crypto/sha2/sha2.c optional crypto | geom_bde | ipsec | random | \
crypto/sha2/sha2.c optional crypto | geom_bde | ipsec | random random_yarrow | random !random_yarrow !random_dummy | \
sctp | zfs
crypto/sha2/sha256c.c optional crypto | geom_bde | ipsec | random | \
crypto/sha2/sha256c.c optional crypto | geom_bde | ipsec | random random_yarrow | random !random_yarrow !random_dummy | \
sctp | zfs
crypto/siphash/siphash.c optional inet | inet6
crypto/siphash/siphash_test.c optional inet | inet6
@ -2139,15 +2139,12 @@ rt2860.fw optional rt2860fw | ralfw \
compile-with "${NORMAL_FW}" \
no-obj no-implicit-rule \
clean "rt2860.fw"
dev/random/randomdev.c standard
dev/random/random_adaptors.c standard
dev/random/dummy_rng.c standard
dev/random/live_entropy_sources.c standard
dev/random/random_harvestq.c standard
dev/random/randomdev_soft.c optional random
dev/random/yarrow.c optional random
dev/random/fortuna.c optional random
dev/random/hash.c optional random
dev/random/randomdev_none.c optional !random
dev/random/randomdev.c optional random
dev/random/random_harvestq.c optional random random_yarrow | random !random_dummy
dev/random/yarrow.c optional random random_yarrow
dev/random/fortuna.c optional random !random_yarrow !random_dummy
dev/random/hash.c optional random random_yarrow | random !random_dummy
dev/rc/rc.c optional rc
dev/re/if_re.c optional re
dev/rl/if_rl.c optional rl pci

View File

@ -939,9 +939,16 @@ RACCT_DEFAULT_TO_DISABLED opt_global.h
RCTL opt_global.h
# Random number generator(s)
# The DEBUG option is in global.h as the random harvesting
# puts probes all over the place, and it makes little sense
# to pollute these headers with an extra include.
# the DUMMY option is in global.h because it is used to
# turn off harvesting all over the kernel.
RANDOM_DEBUG opt_global.h
# Which CSPRNG hashes we get.
# These are mutually exclusive. With neither, Fortuna is selected.
RANDOM_DUMMY opt_global.h
RANDOM_YARROW opt_random.h
RANDOM_FORTUNA opt_random.h
RANDOM_DEBUG opt_random.h
# Intel em(4) driver
EM_MULTIQUEUE opt_em.h

View File

@ -476,7 +476,8 @@ glxsb_rnd(void *v)
if (status & SB_RNS_TRNG_VALID) {
value = bus_read_4(sc->sc_sr, SB_RANDOM_NUM);
/* feed with one uint32 */
random_harvest(&value, sizeof(value), 32/2, RANDOM_PURE_GLXSB);
/* MarkM: FIX!! Check that this does not swamp the harvester! */
random_harvest_queue(&value, sizeof(value), 32/2, RANDOM_PURE_GLXSB);
}
callout_reset(&sc->sc_rngco, sc->sc_rnghz, glxsb_rnd, sc);

View File

@ -258,7 +258,8 @@ hifn_partname(struct hifn_softc *sc)
static void
default_harvest(struct rndtest_state *rsp, void *buf, u_int count)
{
random_harvest(buf, count, count*NBBY/2, RANDOM_PURE_HIFN);
/* MarkM: FIX!! Check that this does not swamp the harvester! */
random_harvest_queue(buf, count, count*NBBY/2, RANDOM_PURE_HIFN);
}
static u_int

View File

@ -1,3 +1,29 @@
#!/bin/sh
#-
# Copyright (c) 2013-2015 Mark R V Murray
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer
# in this position and unchanged.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# $FreeBSD$
#
# Basic script to build crude unit tests.
@ -11,6 +37,7 @@ cc -g -O0 -pthread -DRANDOM_DEBUG -DRANDOM_YARROW \
../../crypto/rijndael/rijndael-alg-fst.c \
../../crypto/sha2/sha2.c \
../../crypto/sha2/sha256c.c \
-lz \
-o yunit_test
cc -g -O0 -pthread -DRANDOM_DEBUG -DRANDOM_FORTUNA \
-I../.. -lstdthreads -Wall \
@ -21,4 +48,5 @@ cc -g -O0 -pthread -DRANDOM_DEBUG -DRANDOM_FORTUNA \
../../crypto/rijndael/rijndael-alg-fst.c \
../../crypto/sha2/sha2.c \
../../crypto/sha2/sha256c.c \
-lz \
-o funit_test

View File

@ -1,111 +0,0 @@
/*-
* Copyright (c) 2013 Arthur Mesh <arthurmesh@gmail.com>
* Copyright (c) 2013 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_random.h"
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/random.h>
#include <sys/syslog.h>
#include <sys/systm.h>
#include <dev/random/randomdev.h>
#include <dev/random/random_adaptors.h>
static int
dummy_random_zero(void)
{
return (0);
}
static void
dummy_random(void)
{
}
/* ARGSUSED */
static void
dummy_random_init(void)
{
#ifdef RANDOM_DEBUG
printf("random: %s\n", __func__);
#endif
randomdev_init_reader(dummy_random_read_phony);
}
/* This is used only by the internal read_random(9) call, and then only
* if no entropy processor is loaded.
*
* Make a token effort to provide _some_ kind of output. No warranty of
* the quality of this output is made, mainly because its lousy.
*
* This is only used by the internal read_random(9) call when no other
* adaptor is active.
*
* It has external scope due to the way things work in
* randomdev_[de]init_reader() that the rest of the world doesn't need to
* know about.
*
* Caveat Emptor.
*/
void
dummy_random_read_phony(uint8_t *buf, u_int count)
{
/* If no entropy device is loaded, don't spam the console with warnings */
u_long randval;
size_t size, i;
/* srandom() is called in kern/init_main.c:proc0_post() */
/* Fill buf[] with random(9) output */
for (i = 0; i < count; i += sizeof(randval)) {
randval = random();
size = MIN(count - i, sizeof(randval));
memcpy(buf + i, &randval, (size_t)size);
}
}
struct random_adaptor randomdev_dummy = {
.ra_ident = "Dummy",
.ra_priority = 1, /* Bottom priority, so goes to last position */
.ra_reseed = dummy_random,
.ra_seeded = (random_adaptor_seeded_func_t *)dummy_random_zero,
.ra_read = (random_adaptor_read_func_t *)dummy_random_zero,
.ra_write = (random_adaptor_write_func_t *)dummy_random_zero,
.ra_init = dummy_random_init,
.ra_deinit = dummy_random,
};

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2013-2014 Mark R V Murray
* Copyright (c) 2013-2015 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -25,25 +25,24 @@
*
*/
/* This implementation of Fortuna is based on the descriptions found in
* ISBN 0-471-22357-3 "Practical Cryptography" by Ferguson and Schneier
* ("F&S").
*
* The above book is superseded by ISBN 978-0-470-47424-2 "Cryptography
* Engineering" by Ferguson, Schneier and Kohno ("FS&K"). The code has
* not yet fully caught up with FS&K.
/*
* This implementation of Fortuna is based on the descriptions found in
* ISBN 978-0-470-47424-2 "Cryptography Engineering" by Ferguson, Schneier
* and Kohno ("FS&K").
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#ifdef _KERNEL
#include "opt_random.h"
#include <sys/limits.h>
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/random.h>
#include <sys/sysctl.h>
@ -56,13 +55,10 @@ __FBSDID("$FreeBSD$");
#include <dev/random/hash.h>
#include <dev/random/randomdev.h>
#include <dev/random/random_adaptors.h>
#include <dev/random/random_harvestq.h>
#include <dev/random/uint128.h>
#include <dev/random/fortuna.h>
#else /* !_KERNEL */
#include <sys/param.h>
#include <sys/types.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
@ -79,351 +75,405 @@ __FBSDID("$FreeBSD$");
#include <dev/random/fortuna.h>
#endif /* _KERNEL */
#if !defined(RANDOM_YARROW) && !defined(RANDOM_FORTUNA)
#define RANDOM_YARROW
#elif defined(RANDOM_YARROW) && defined(RANDOM_FORTUNA)
#error "Must define either RANDOM_YARROW or RANDOM_FORTUNA"
#endif
/* Defined in FS&K */
#define RANDOM_FORTUNA_NPOOLS 32 /* The number of accumulation pools */
#define RANDOM_FORTUNA_DEFPOOLSIZE 64 /* The default pool size/length for a (re)seed */
#define RANDOM_FORTUNA_MAX_READ (1 << 20) /* Max bytes in a single read */
#if defined(RANDOM_FORTUNA)
/*
* The allowable range of RANDOM_FORTUNA_DEFPOOLSIZE. The default value is above.
* Making RANDOM_FORTUNA_DEFPOOLSIZE too large will mean a long time between reseeds,
* and too small may compromise initial security but get faster reseeds.
*/
#define RANDOM_FORTUNA_MINPOOLSIZE 16
#define RANDOM_FORTUNA_MAXPOOLSIZE UINT_MAX
CTASSERT(RANDOM_FORTUNA_MINPOOLSIZE <= RANDOM_FORTUNA_DEFPOOLSIZE);
CTASSERT(RANDOM_FORTUNA_DEFPOOLSIZE <= RANDOM_FORTUNA_MAXPOOLSIZE);
#define NPOOLS 32
#define MINPOOLSIZE 64
#define DEFPOOLSIZE 256
#define MAXPOOLSIZE 65536
/* This algorithm (and code) presumes that RANDOM_KEYSIZE is twice as large as RANDOM_BLOCKSIZE */
CTASSERT(RANDOM_BLOCKSIZE == sizeof(uint128_t));
CTASSERT(RANDOM_KEYSIZE == 2*RANDOM_BLOCKSIZE);
/* This algorithm (and code) presumes that KEYSIZE is twice as large as BLOCKSIZE */
CTASSERT(BLOCKSIZE == sizeof(uint128_t));
CTASSERT(KEYSIZE == 2*BLOCKSIZE);
/* This is the beastie that needs protecting. It contains all of the
* state that we are excited about.
* Exactly one is instantiated.
/*
* This is the beastie that needs protecting. It contains all of the
* state that we are excited about. Exactly one is instantiated.
*/
static struct fortuna_state {
/* P_i */
struct pool {
u_int length;
struct randomdev_hash hash;
} pool[NPOOLS];
/* ReseedCnt */
u_int reseedcount;
/* C - 128 bits */
union {
uint8_t byte[BLOCKSIZE];
uint128_t whole;
} counter;
/* K */
struct randomdev_key key;
/* Extras */
u_int minpoolsize;
struct fs_pool { /* P_i */
u_int fsp_length; /* Only the first one is used by Fortuna */
struct randomdev_hash fsp_hash;
} fs_pool[RANDOM_FORTUNA_NPOOLS];
u_int fs_reseedcount; /* ReseedCnt */
uint128_t fs_counter; /* C */
struct randomdev_key fs_key; /* K */
u_int fs_minpoolsize; /* Extras */
/* Extras for the OS */
#ifdef _KERNEL
/* For use when 'pacing' the reseeds */
sbintime_t lasttime;
sbintime_t fs_lasttime;
#endif
/* Reseed lock */
mtx_t fs_mtx;
} fortuna_state;
/* The random_reseed_mtx mutex protects seeding and polling/blocking. */
static mtx_t random_reseed_mtx;
static struct fortuna_start_cache {
uint8_t junk[PAGE_SIZE];
size_t length;
struct randomdev_hash hash;
} fortuna_start_cache;
#ifdef _KERNEL
static struct sysctl_ctx_list random_clist;
RANDOM_CHECK_UINT(minpoolsize, MINPOOLSIZE, MAXPOOLSIZE);
RANDOM_CHECK_UINT(fs_minpoolsize, RANDOM_FORTUNA_MINPOOLSIZE, RANDOM_FORTUNA_MAXPOOLSIZE);
#else
static uint8_t zero_region[RANDOM_ZERO_BLOCKSIZE];
#endif
void
random_fortuna_init_alg(void)
static void random_fortuna_pre_read(void);
static void random_fortuna_read(uint8_t *, u_int);
static void random_fortuna_post_read(void);
static void random_fortuna_write(uint8_t *, u_int);
static void random_fortuna_reseed(void);
static int random_fortuna_seeded(void);
static void random_fortuna_process_event(struct harvest_event *);
#ifdef _KERNEL
/* Interface to Adaptors system */
struct random_algorithm random_alg_context = {
.ra_ident = "Fortuna",
.ra_pre_read = random_fortuna_pre_read,
.ra_read = random_fortuna_read,
.ra_post_read = random_fortuna_post_read,
.ra_write = random_fortuna_write,
.ra_reseed = random_fortuna_reseed,
.ra_seeded = random_fortuna_seeded,
.ra_event_processor = random_fortuna_process_event,
.ra_poolcount = RANDOM_FORTUNA_NPOOLS,
};
#endif
/* ARGSUSED */
static void
random_fortuna_init_alg(void *unused __unused)
{
int i;
#ifdef _KERNEL
struct sysctl_oid *random_fortuna_o;
#endif
memset(fortuna_start_cache.junk, 0, sizeof(fortuna_start_cache.junk));
fortuna_start_cache.length = 0U;
randomdev_hash_init(&fortuna_start_cache.hash);
/* Set up a lock for the reseed process */
#ifdef _KERNEL
mtx_init(&random_reseed_mtx, "reseed mutex", NULL, MTX_DEF);
#else /* !_KERNEL */
mtx_init(&random_reseed_mtx, mtx_plain);
#endif /* _KERNEL */
#ifdef _KERNEL
/* Fortuna parameters. Do not adjust these unless you have
RANDOM_RESEED_INIT_LOCK();
/*
* Fortuna parameters. Do not adjust these unless you have
* have a very good clue about what they do!
*/
fortuna_state.fs_minpoolsize = RANDOM_FORTUNA_DEFPOOLSIZE;
#ifdef _KERNEL
fortuna_state.fs_lasttime = 0;
random_fortuna_o = SYSCTL_ADD_NODE(&random_clist,
SYSCTL_STATIC_CHILDREN(_kern_random),
OID_AUTO, "fortuna", CTLFLAG_RW, 0,
"Fortuna Parameters");
SYSCTL_ADD_PROC(&random_clist,
SYSCTL_CHILDREN(random_fortuna_o), OID_AUTO,
"minpoolsize", CTLTYPE_UINT|CTLFLAG_RW,
&fortuna_state.minpoolsize, DEFPOOLSIZE,
random_check_uint_minpoolsize, "IU",
"Minimum pool size necessary to cause a reseed automatically");
fortuna_state.lasttime = 0U;
"minpoolsize", CTLTYPE_UINT | CTLFLAG_RWTUN,
&fortuna_state.fs_minpoolsize, RANDOM_FORTUNA_DEFPOOLSIZE,
random_check_uint_fs_minpoolsize, "IU",
"Minimum pool size necessary to cause a reseed");
KASSERT(fortuna_state.fs_minpoolsize > 0, ("random: Fortuna threshold must be > 0 at startup"));
#endif
fortuna_state.minpoolsize = DEFPOOLSIZE;
/* F&S - InitializePRNG() */
/* F&S - P_i = \epsilon */
for (i = 0; i < NPOOLS; i++) {
randomdev_hash_init(&fortuna_state.pool[i].hash);
fortuna_state.pool[i].length = 0U;
/*-
* FS&K - InitializePRNG()
* - P_i = \epsilon
* - ReseedCNT = 0
*/
for (i = 0; i < RANDOM_FORTUNA_NPOOLS; i++) {
randomdev_hash_init(&fortuna_state.fs_pool[i].fsp_hash);
fortuna_state.fs_pool[i].fsp_length = 0;
}
/* F&S - ReseedCNT = 0 */
fortuna_state.reseedcount = 0U;
/* F&S - InitializeGenerator() */
/* F&S - C = 0 */
uint128_clear(&fortuna_state.counter.whole);
/* F&S - K = 0 */
memset(&fortuna_state.key, 0, sizeof(fortuna_state.key));
fortuna_state.fs_reseedcount = 0;
/*-
* FS&K - InitializeGenerator()
* - C = 0
* - K = 0
*/
fortuna_state.fs_counter = UINT128_ZERO;
explicit_bzero(&fortuna_state.fs_key, sizeof(fortuna_state.fs_key));
}
#ifdef _KERNEL
SYSINIT(random_fortuna, SI_SUB_RANDOM, SI_ORDER_THIRD, random_fortuna_init_alg, NULL);
#endif
void
random_fortuna_deinit_alg(void)
/* ARGSUSED */
static void
random_fortuna_deinit_alg(void *unused __unused)
{
mtx_destroy(&random_reseed_mtx);
memset(&fortuna_state, 0, sizeof(fortuna_state));
RANDOM_RESEED_DEINIT_LOCK();
explicit_bzero(&fortuna_state, sizeof(fortuna_state));
#ifdef _KERNEL
sysctl_ctx_free(&random_clist);
#endif
}
#ifdef _KERNEL
SYSUNINIT(random_fortuna, SI_SUB_RANDOM, SI_ORDER_THIRD, random_fortuna_deinit_alg, NULL);
#endif
/* F&S - AddRandomEvent() */
/* Process a single stochastic event off the harvest queue */
/*-
* FS&K - AddRandomEvent()
* Process a single stochastic event off the harvest queue
*/
void
random_fortuna_process_event(struct harvest_event *event)
{
u_int pl;
/* We must be locked for all this as plenty of state gets messed with */
mtx_lock(&random_reseed_mtx);
/* Accumulate the event into the appropriate pool
* where each event carries the destination information
RANDOM_RESEED_LOCK();
/*-
* FS&K - P_i = P_i|<harvested stuff>
* Accumulate the event into the appropriate pool
* where each event carries the destination information.
*
* The hash_init() and hash_finish() calls are done in
* random_fortuna_pre_read().
*
* We must be locked against pool state modification which can happen
* during accumulation/reseeding and reading/regating.
*/
/* F&S - P_i = P_i|<harvested stuff> */
/* The hash_init and hash_finish are done in random_fortuna_read() below */
pl = event->he_destination % NPOOLS;
randomdev_hash_iterate(&fortuna_state.pool[pl].hash, event, sizeof(*event));
/* No point in counting above the outside maximum */
fortuna_state.pool[pl].length += event->he_size;
fortuna_state.pool[pl].length = MIN(fortuna_state.pool[pl].length, MAXPOOLSIZE);
/* Done with state-messing */
mtx_unlock(&random_reseed_mtx);
pl = event->he_destination % RANDOM_FORTUNA_NPOOLS;
randomdev_hash_iterate(&fortuna_state.fs_pool[pl].fsp_hash, event, sizeof(*event));
/*-
* Don't wrap the length. Doing the the hard way so as not to wrap at MAXUINT.
* This is a "saturating" add.
* XXX: FIX!!: We don't actually need lengths for anything but fs_pool[0],
* but it's been useful debugging to see them all.
*/
if (RANDOM_FORTUNA_MAXPOOLSIZE - fortuna_state.fs_pool[pl].fsp_length > event->he_size)
fortuna_state.fs_pool[pl].fsp_length += event->he_size;
else
fortuna_state.fs_pool[pl].fsp_length = RANDOM_FORTUNA_MAXPOOLSIZE;
explicit_bzero(event, sizeof(*event));
RANDOM_RESEED_UNLOCK();
}
/* F&S - Reseed() */
/* Reseed Mutex is held */
/*-
* Process a block of data suspected to be slightly stochastic.
* Do this by breaking it up and inserting the pieces as if
* they were separate events.
*/
static void
reseed(uint8_t *junk, u_int length)
random_fortuna_process_buffer(uint32_t *buf, u_int wordcount)
{
static struct harvest_event event;
static u_int destination = 0;
int i;
for (i = 0; i < wordcount; i += sizeof(event.he_entropy)/sizeof(event.he_entropy[0])) {
event.he_somecounter = (uint32_t)get_cyclecount();
event.he_size = sizeof(event.he_entropy);
event.he_bits = event.he_size/8;
event.he_source = RANDOM_CACHED;
event.he_destination = destination++; /* Harmless cheating */
memcpy(event.he_entropy, buf + i, sizeof(event.he_entropy));
random_fortuna_process_event(&event);
}
}
/*-
* FS&K - Reseed()
* This introduces new key material into the output generator.
* Additionaly it increments the output generator's counter
* variable C. When C > 0, the output generator is seeded and
* will deliver output.
* The entropy_data buffer passed is a very specific size; the
* product of RANDOM_FORTUNA_NPOOLS and RANDOM_KEYSIZE.
*/
static void
random_fortuna_reseed_internal(uint32_t *entropy_data, u_int blockcount)
{
struct randomdev_hash context;
uint8_t hash[KEYSIZE];
uint8_t hash[RANDOM_KEYSIZE];
KASSERT(fortuna_state.minpoolsize > 0, ("random: Fortuna threshold = 0"));
#ifdef _KERNEL
mtx_assert(&random_reseed_mtx, MA_OWNED);
#endif
/* FS&K - K = Hd(K|s) where Hd(m) is H(H(0^512|m)) */
RANDOM_RESEED_ASSERT_LOCK_OWNED();
/*-
* FS&K - K = Hd(K|s) where Hd(m) is H(H(0^512|m))
* - C = C + 1
*/
randomdev_hash_init(&context);
randomdev_hash_iterate(&context, zero_region, 512/8);
randomdev_hash_iterate(&context, &fortuna_state.key, sizeof(fortuna_state.key));
randomdev_hash_iterate(&context, junk, length);
randomdev_hash_iterate(&context, zero_region, RANDOM_ZERO_BLOCKSIZE);
randomdev_hash_iterate(&context, &fortuna_state.fs_key, sizeof(fortuna_state.fs_key));
randomdev_hash_iterate(&context, entropy_data, RANDOM_KEYSIZE*blockcount);
randomdev_hash_finish(&context, hash);
randomdev_hash_init(&context);
randomdev_hash_iterate(&context, hash, KEYSIZE);
randomdev_hash_iterate(&context, hash, RANDOM_KEYSIZE);
randomdev_hash_finish(&context, hash);
randomdev_encrypt_init(&fortuna_state.key, hash);
memset(hash, 0, sizeof(hash));
/* Unblock the device if it was blocked due to being unseeded */
if (uint128_is_zero(fortuna_state.counter.whole))
random_adaptor_unblock();
/* FS&K - C = C + 1 */
uint128_increment(&fortuna_state.counter.whole);
randomdev_encrypt_init(&fortuna_state.fs_key, hash);
explicit_bzero(hash, sizeof(hash));
/* Unblock the device if this is the first time we are reseeding. */
if (uint128_is_zero(fortuna_state.fs_counter))
randomdev_unblock();
uint128_increment(&fortuna_state.fs_counter);
}
/* F&S - GenerateBlocks() */
/* Reseed Mutex is held, and buf points to a whole number of blocks. */
/*-
* FS&K - GenerateBlocks()
* Generate a number of complete blocks of random output.
*/
static __inline void
random_fortuna_genblocks(uint8_t *buf, u_int blockcount)
{
u_int i;
for (i = 0u; i < blockcount; i++) {
/* F&S - r = r|E(K,C) */
randomdev_encrypt(&fortuna_state.key, fortuna_state.counter.byte, buf, BLOCKSIZE);
buf += BLOCKSIZE;
/* F&S - C = C + 1 */
uint128_increment(&fortuna_state.counter.whole);
RANDOM_RESEED_ASSERT_LOCK_OWNED();
for (i = 0; i < blockcount; i++) {
/*-
* FS&K - r = r|E(K,C)
* - C = C + 1
*/
randomdev_encrypt(&fortuna_state.fs_key, &fortuna_state.fs_counter, buf, RANDOM_BLOCKSIZE);
buf += RANDOM_BLOCKSIZE;
uint128_increment(&fortuna_state.fs_counter);
}
}
/* F&S - PseudoRandomData() */
/* Reseed Mutex is held, and buf points to a whole number of blocks. */
/*-
* FS&K - PseudoRandomData()
* This generates no more than 2^20 bytes of data, and cleans up its
* internal state when finished. It is assumed that a whole number of
* blocks are available for writing; any excess generated will be
* ignored.
*/
static __inline void
random_fortuna_genrandom(uint8_t *buf, u_int bytecount)
{
static uint8_t temp[BLOCKSIZE*(KEYSIZE/BLOCKSIZE)];
static uint8_t temp[RANDOM_BLOCKSIZE*(RANDOM_KEYS_PER_BLOCK)];
u_int blockcount;
/* F&S - assert(n < 2^20) */
KASSERT((bytecount <= (1 << 20)), ("invalid single read request to fortuna of %d bytes", bytecount));
/* F&S - r = first-n-bytes(GenerateBlocks(ceil(n/16))) */
blockcount = bytecount / BLOCKSIZE;
RANDOM_RESEED_ASSERT_LOCK_OWNED();
/*-
* FS&K - assert(n < 2^20 (== 1 MB)
* - r = first-n-bytes(GenerateBlocks(ceil(n/16)))
* - K = GenerateBlocks(2)
*/
KASSERT((bytecount <= RANDOM_FORTUNA_MAX_READ), ("invalid single read request to Fortuna of %d bytes", bytecount));
blockcount = (bytecount + RANDOM_BLOCKSIZE - 1)/RANDOM_BLOCKSIZE;
random_fortuna_genblocks(buf, blockcount);
/* TODO: FIX! remove memcpy()! */
if (bytecount % BLOCKSIZE > 0) {
random_fortuna_genblocks(temp, 1);
memcpy(buf + (blockcount * BLOCKSIZE), temp, bytecount % BLOCKSIZE);
}
/* F&S - K = GenerateBlocks(2) */
random_fortuna_genblocks(temp, KEYSIZE/BLOCKSIZE);
randomdev_encrypt_init(&fortuna_state.key, temp);
memset(temp, 0, sizeof(temp));
random_fortuna_genblocks(temp, RANDOM_KEYS_PER_BLOCK);
randomdev_encrypt_init(&fortuna_state.fs_key, temp);
explicit_bzero(temp, sizeof(temp));
}
/* F&S - RandomData() */
/* Used to return processed entropy from the PRNG */
/* The argument buf points to a whole number of blocks. */
/*-
* FS&K - RandomData()
* Used to return processed entropy from the PRNG.
* There is a pre_read and a post_read required to be present
* (but they can be null functions) in order to allow specific
* actions at the begin or the end of a read. Fortuna does its
* reseeding in the _pre_read() part, and _post_read() is not
* used.
*/
void
random_fortuna_pre_read(void)
{
#ifdef _KERNEL
sbintime_t now;
#endif
struct randomdev_hash context;
uint32_t s[RANDOM_FORTUNA_NPOOLS*RANDOM_KEYSIZE_WORDS];
uint8_t temp[RANDOM_KEYSIZE];
u_int i;
KASSERT(fortuna_state.fs_minpoolsize > 0, ("random: Fortuna threshold must be > 0"));
#ifdef _KERNEL
/* FS&K - Use 'getsbinuptime()' to prevent reseed-spamming. */
now = getsbinuptime();
#endif
RANDOM_RESEED_LOCK();
if (fortuna_state.fs_pool[0].fsp_length >= fortuna_state.fs_minpoolsize
#ifdef _KERNEL
/* FS&K - Use 'getsbinuptime()' to prevent reseed-spamming. */
&& (now - fortuna_state.fs_lasttime > hz/10)
#endif
) {
#ifdef _KERNEL
fortuna_state.fs_lasttime = now;
#endif
/* FS&K - ReseedCNT = ReseedCNT + 1 */
fortuna_state.fs_reseedcount++;
/* s = \epsilon at start */
for (i = 0; i < RANDOM_FORTUNA_NPOOLS; i++) {
/* FS&K - if Divides(ReseedCnt, 2^i) ... */
if ((fortuna_state.fs_reseedcount % (1 << i)) == 0) {
/*-
* FS&K - temp = (P_i)
* - P_i = \epsilon
* - s = s|H(temp)
*/
randomdev_hash_finish(&fortuna_state.fs_pool[i].fsp_hash, temp);
randomdev_hash_init(&fortuna_state.fs_pool[i].fsp_hash);
fortuna_state.fs_pool[i].fsp_length = 0;
randomdev_hash_init(&context);
randomdev_hash_iterate(&context, temp, RANDOM_KEYSIZE);
randomdev_hash_finish(&context, s + i*RANDOM_KEYSIZE_WORDS);
} else
break;
}
#ifdef RANDOM_DEBUG
{
u_int j;
printf("random: reseedcount [%d]", fortuna_state.fs_reseedcount);
for (j = 0; j < RANDOM_FORTUNA_NPOOLS; j++)
printf(" %X", fortuna_state.fs_pool[j].fsp_length);
printf("\n");
}
#endif
/* FS&K */
random_fortuna_reseed_internal(s, i < RANDOM_FORTUNA_NPOOLS ? i + 1 : RANDOM_FORTUNA_NPOOLS);
/* Clean up and secure */
explicit_bzero(s, sizeof(s));
explicit_bzero(temp, sizeof(temp));
explicit_bzero(&context, sizeof(context));
}
RANDOM_RESEED_UNLOCK();
}
/*-
* Main read from Fortuna.
* The supplied buf MUST be a multiple (>=0) of RANDOM_BLOCKSIZE in size.
* Lots of code presumes this for efficiency, both here and in other
* routines. You are NOT allowed to break this!
*/
void
random_fortuna_read(uint8_t *buf, u_int bytecount)
{
#ifdef _KERNEL
sbintime_t thistime;
#endif
struct randomdev_hash context;
uint8_t s[NPOOLS*KEYSIZE], temp[KEYSIZE];
int i;
u_int seedlength;
/* We must be locked for all this as plenty of state gets messed with */
mtx_lock(&random_reseed_mtx);
/* if buf == NULL and bytecount == 0 then this is the pre-read. */
/* if buf == NULL and bytecount != 0 then this is the post-read; ignore. */
if (buf == NULL) {
if (bytecount == 0) {
if (fortuna_state.pool[0].length >= fortuna_state.minpoolsize
#ifdef _KERNEL
/* F&S - Use 'getsbinuptime()' to prevent reseed-spamming. */
&& ((thistime = getsbinuptime()) - fortuna_state.lasttime > hz/10)
#endif
) {
#ifdef _KERNEL
fortuna_state.lasttime = thistime;
#endif
seedlength = 0U;
/* F&S - ReseedCNT = ReseedCNT + 1 */
fortuna_state.reseedcount++;
/* s = \epsilon by default */
for (i = 0; i < NPOOLS; i++) {
/* F&S - if Divides(ReseedCnt, 2^i) ... */
if ((fortuna_state.reseedcount % (1 << i)) == 0U) {
seedlength += KEYSIZE;
/* F&S - temp = (P_i) */
randomdev_hash_finish(&fortuna_state.pool[i].hash, temp);
/* F&S - P_i = \epsilon */
randomdev_hash_init(&fortuna_state.pool[i].hash);
fortuna_state.pool[i].length = 0U;
/* F&S - s = s|H(temp) */
randomdev_hash_init(&context);
randomdev_hash_iterate(&context, temp, KEYSIZE);
randomdev_hash_finish(&context, s + i*KEYSIZE);
}
else
break;
}
#ifdef RANDOM_DEBUG
printf("random: active reseed: reseedcount [%d] ", fortuna_state.reseedcount);
for (i = 0; i < NPOOLS; i++)
printf(" %d", fortuna_state.pool[i].length);
printf("\n");
#endif
/* F&S */
reseed(s, seedlength);
/* Clean up */
memset(s, 0, seedlength);
seedlength = 0U;
memset(temp, 0, sizeof(temp));
memset(&context, 0, sizeof(context));
}
}
}
/* if buf != NULL do a regular read. */
else
random_fortuna_genrandom(buf, bytecount);
mtx_unlock(&random_reseed_mtx);
RANDOM_RESEED_LOCK();
random_fortuna_genrandom(buf, bytecount);
RANDOM_RESEED_UNLOCK();
}
/* Internal function to hand external entropy to the PRNG */
void
random_fortuna_post_read(void)
{
/* CWOT */
}
/* Internal function to hand external entropy to the PRNG. */
void
random_fortuna_write(uint8_t *buf, u_int count)
{
uint8_t temp[KEYSIZE];
int i;
uintmax_t timestamp;
struct randomdev_hash hash;
uint32_t entropy_data[RANDOM_KEYSIZE_WORDS], timestamp;
timestamp = get_cyclecount();
randomdev_hash_iterate(&fortuna_start_cache.hash, &timestamp, sizeof(timestamp));
randomdev_hash_iterate(&fortuna_start_cache.hash, buf, count);
timestamp = get_cyclecount();
randomdev_hash_iterate(&fortuna_start_cache.hash, &timestamp, sizeof(timestamp));
randomdev_hash_finish(&fortuna_start_cache.hash, temp);
for (i = 0; i < KEYSIZE; i++)
fortuna_start_cache.junk[(fortuna_start_cache.length + i)%PAGE_SIZE] ^= temp[i];
fortuna_start_cache.length += KEYSIZE;
#ifdef RANDOM_DEBUG
printf("random: %s - ", __func__);
for (i = 0; i < KEYSIZE; i++)
printf("%02X", temp[i]);
printf("\n");
#endif
memset(temp, 0, KEYSIZE);
/* We must be locked for all this as plenty of state gets messed with */
mtx_lock(&random_reseed_mtx);
randomdev_hash_init(&fortuna_start_cache.hash);
reseed(fortuna_start_cache.junk, MIN(PAGE_SIZE, fortuna_start_cache.length));
memset(fortuna_start_cache.junk, 0, sizeof(fortuna_start_cache.junk));
mtx_unlock(&random_reseed_mtx);
/* Extra timing here is helpful to scrape scheduler timing entropy */
randomdev_hash_init(&hash);
timestamp = (uint32_t)get_cyclecount();
randomdev_hash_iterate(&hash, &timestamp, sizeof(timestamp));
randomdev_hash_iterate(&hash, buf, count);
timestamp = (uint32_t)get_cyclecount();
randomdev_hash_iterate(&hash, &timestamp, sizeof(timestamp));
randomdev_hash_finish(&hash, entropy_data);
explicit_bzero(&hash, sizeof(hash));
random_fortuna_process_buffer(entropy_data, sizeof(entropy_data)/sizeof(entropy_data[0]));
explicit_bzero(entropy_data, sizeof(entropy_data));
}
void
@ -437,7 +487,5 @@ int
random_fortuna_seeded(void)
{
return (!uint128_is_zero(fortuna_state.counter.whole));
return (!uint128_is_zero(fortuna_state.fs_counter));
}
#endif /* RANDOM_FORTUNA */

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2013 Mark R V Murray
* Copyright (c) 2013-2015 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -27,18 +27,21 @@
*/
#ifndef SYS_DEV_RANDOM_FORTUNA_H_INCLUDED
#define SYS_DEV_RANDOM_FORTUNA_H_INCLUDED
#define SYS_DEV_RANDOM_FORTUNA_H_INCLUDED
#ifdef _KERNEL
typedef struct mtx mtx_t;
#define RANDOM_RESEED_INIT_LOCK(x) mtx_init(&fortuna_state.fs_mtx, "reseed mutex", NULL, MTX_DEF)
#define RANDOM_RESEED_DEINIT_LOCK(x) mtx_destroy(&fortuna_state.fs_mtx)
#define RANDOM_RESEED_LOCK(x) mtx_lock(&fortuna_state.fs_mtx)
#define RANDOM_RESEED_UNLOCK(x) mtx_unlock(&fortuna_state.fs_mtx)
#define RANDOM_RESEED_ASSERT_LOCK_OWNED(x) mtx_assert(&fortuna_state.fs_mtx, MA_OWNED)
#else
#define RANDOM_RESEED_INIT_LOCK(x) mtx_init(&fortuna_state.fs_mtx, mtx_plain)
#define RANDOM_RESEED_DEINIT_LOCK(x) mtx_destroy(&fortuna_state.fs_mtx)
#define RANDOM_RESEED_LOCK(x) mtx_lock(&fortuna_state.fs_mtx)
#define RANDOM_RESEED_UNLOCK(x) mtx_unlock(&fortuna_state.fs_mtx)
#define RANDOM_RESEED_ASSERT_LOCK_OWNED(x)
#endif
void random_fortuna_init_alg(void);
void random_fortuna_deinit_alg(void);
void random_fortuna_read(uint8_t *, u_int);
void random_fortuna_write(uint8_t *, u_int);
void random_fortuna_reseed(void);
int random_fortuna_seeded(void);
void random_fortuna_process_event(struct harvest_event *event);
#endif
#endif /* SYS_DEV_RANDOM_FORTUNA_H_INCLUDED */

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2000-2013 Mark R V Murray
* Copyright (c) 2000-2015 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -47,8 +47,8 @@ __FBSDID("$FreeBSD$");
#include <dev/random/hash.h>
/* This code presumes that KEYSIZE is twice as large as BLOCKSIZE */
CTASSERT(KEYSIZE == 2*BLOCKSIZE);
/* This code presumes that RANDOM_KEYSIZE is twice as large as RANDOM_BLOCKSIZE */
CTASSERT(RANDOM_KEYSIZE == 2*RANDOM_BLOCKSIZE);
/* Initialise the hash */
void
@ -67,7 +67,7 @@ randomdev_hash_iterate(struct randomdev_hash *context, const void *data, size_t
}
/* Conclude by returning the hash in the supplied <*buf> which must be
* KEYSIZE bytes long.
* RANDOM_KEYSIZE bytes long.
*/
void
randomdev_hash_finish(struct randomdev_hash *context, void *buf)
@ -77,20 +77,20 @@ randomdev_hash_finish(struct randomdev_hash *context, void *buf)
}
/* Initialise the encryption routine by setting up the key schedule
* from the supplied <*data> which must be KEYSIZE bytes of binary
* data. Use CBC mode for better avalanche.
* from the supplied <*data> which must be RANDOM_KEYSIZE bytes of binary
* data.
*/
void
randomdev_encrypt_init(struct randomdev_key *context, const void *data)
{
rijndael_cipherInit(&context->cipher, MODE_CBC, NULL);
rijndael_makeKey(&context->key, DIR_ENCRYPT, KEYSIZE*8, data);
rijndael_cipherInit(&context->cipher, MODE_ECB, NULL);
rijndael_makeKey(&context->key, DIR_ENCRYPT, RANDOM_KEYSIZE*8, data);
}
/* Encrypt the supplied data using the key schedule preset in the context.
* <length> bytes are encrypted from <*d_in> to <*d_out>. <length> must be
* a multiple of BLOCKSIZE.
* a multiple of RANDOM_BLOCKSIZE.
*/
void
randomdev_encrypt(struct randomdev_key *context, const void *d_in, void *d_out, u_int length)

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2000-2013 Mark R V Murray
* Copyright (c) 2000-2015 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -27,10 +27,14 @@
*/
#ifndef SYS_DEV_RANDOM_HASH_H_INCLUDED
#define SYS_DEV_RANDOM_HASH_H_INCLUDED
#define SYS_DEV_RANDOM_HASH_H_INCLUDED
#define KEYSIZE 32 /* (in bytes) == 256 bits */
#define BLOCKSIZE 16 /* (in bytes) == 128 bits */
#define RANDOM_KEYSIZE 32 /* (in bytes) == 256 bits */
#define RANDOM_KEYSIZE_WORDS (RANDOM_KEYSIZE/sizeof(uint32_t))
#define RANDOM_BLOCKSIZE 16 /* (in bytes) == 128 bits */
#define RANDOM_BLOCKSIZE_WORDS (RANDOM_BLOCKSIZE/sizeof(uint32_t))
#define RANDOM_KEYS_PER_BLOCK (RANDOM_KEYSIZE/RANDOM_BLOCKSIZE)
#define RANDOM_ZERO_BLOCKSIZE 64 /* (in bytes) == 512 zero bits */
struct randomdev_hash { /* Big! Make static! */
SHA256_CTX sha;
@ -47,4 +51,4 @@ void randomdev_hash_finish(struct randomdev_hash *, void *);
void randomdev_encrypt_init(struct randomdev_key *, const void *);
void randomdev_encrypt(struct randomdev_key *context, const void *, void *, u_int);
#endif
#endif /* SYS_DEV_RANDOM_HASH_H_INCLUDED */

View File

@ -46,18 +46,15 @@ __FBSDID("$FreeBSD$");
#include <machine/specialreg.h>
#include <dev/random/randomdev.h>
#include <dev/random/randomdev_soft.h>
#include <dev/random/random_adaptors.h>
#include <dev/random/live_entropy_sources.h>
#define RETRY_COUNT 10
static u_int random_ivy_read(void *, u_int);
static struct live_entropy_source random_ivy = {
.les_ident = "Intel Secure Key RNG",
.les_source = RANDOM_PURE_RDRAND,
.les_read = random_ivy_read
static struct random_source random_ivy = {
.rs_ident = "Intel Secure Key RNG",
.rs_source = RANDOM_PURE_RDRAND,
.rs_read = random_ivy_read
};
static inline int
@ -108,14 +105,14 @@ rdrand_modevent(module_t mod, int type, void *unused)
switch (type) {
case MOD_LOAD:
if (cpu_feature2 & CPUID2_RDRAND) {
live_entropy_source_register(&random_ivy);
printf("random: live provider: \"%s\"\n", random_ivy.les_ident);
random_source_register(&random_ivy);
printf("random: fast provider: \"%s\"\n", random_ivy.rs_ident);
}
break;
case MOD_UNLOAD:
if (cpu_feature2 & CPUID2_RDRAND)
live_entropy_source_deregister(&random_ivy);
random_source_deregister(&random_ivy);
break;
case MOD_SHUTDOWN:

View File

@ -1,190 +0,0 @@
/*-
* Copyright (c) 2013 Arthur Mesh <arthurmesh@gmail.com>
* Copyright (c) 2013 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
__FBSDID("$FreeBSD$");
#include "opt_random.h"
#include <sys/kernel.h>
#include <sys/libkern.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/queue.h>
#include <sys/random.h>
#include <sys/sbuf.h>
#include <sys/sx.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/unistd.h>
#include <machine/cpu.h>
#include <dev/random/randomdev.h>
#include <dev/random/randomdev_soft.h>
#include <dev/random/random_adaptors.h>
#include <dev/random/random_harvestq.h>
#include "live_entropy_sources.h"
/*
* The les_lock protects the consistency of the "struct les_head les_sources"
*/
static struct sx les_lock; /* Need a sleepable lock for the sbuf/sysctl stuff. */
LIST_HEAD(les_head, live_entropy_sources);
static struct les_head les_sources = LIST_HEAD_INITIALIZER(les_sources);
void
live_entropy_source_register(struct live_entropy_source *rsource)
{
struct live_entropy_sources *lles;
KASSERT(rsource != NULL, ("invalid input to %s", __func__));
lles = malloc(sizeof(*lles), M_ENTROPY, M_WAITOK);
lles->lles_rsource = rsource;
sx_xlock(&les_lock);
LIST_INSERT_HEAD(&les_sources, lles, lles_entries);
sx_xunlock(&les_lock);
}
void
live_entropy_source_deregister(struct live_entropy_source *rsource)
{
struct live_entropy_sources *lles = NULL;
KASSERT(rsource != NULL, ("invalid input to %s", __func__));
sx_xlock(&les_lock);
LIST_FOREACH(lles, &les_sources, lles_entries)
if (lles->lles_rsource == rsource) {
LIST_REMOVE(lles, lles_entries);
break;
}
sx_xunlock(&les_lock);
if (lles != NULL)
free(lles, M_ENTROPY);
}
static int
live_entropy_source_handler(SYSCTL_HANDLER_ARGS)
{
struct live_entropy_sources *lles;
struct sbuf sbuf;
int error, count;
sx_slock(&les_lock);
sbuf_new_for_sysctl(&sbuf, NULL, 64, req);
count = 0;
LIST_FOREACH(lles, &les_sources, lles_entries) {
sbuf_cat(&sbuf, (count++ ? ",'" : "'"));
sbuf_cat(&sbuf, lles->lles_rsource->les_ident);
sbuf_cat(&sbuf, "'");
}
error = sbuf_finish(&sbuf);
sbuf_delete(&sbuf);
sx_sunlock(&les_lock);
return (error);
}
/*
* Run through all "live" sources reading entropy for the given
* number of rounds, which should be a multiple of the number
* of entropy accumulation pools in use; 2 for Yarrow and 32
* for Fortuna.
*
* BEWARE!!!
* This function runs inside the RNG thread! Don't do anything silly!
*/
/* XXXRW: get_cyclecount() is cheap on most modern hardware, where cycle
* counters are built in, but on older hardware it will do a real time clock
* read which can be quite expensive.
*/
void
live_entropy_sources_feed(void)
{
static struct harvest_event event;
struct live_entropy_sources *lles;
int i, read_rate;
u_int n;
sx_slock(&les_lock);
/*
* Walk over all of live entropy sources, and feed their output
* to the system-wide RNG.
*/
read_rate = random_adaptor_read_rate();
LIST_FOREACH(lles, &les_sources, lles_entries) {
for (i = 0; i < harvest_pool_count*read_rate; i++) {
/* This *must* be quick, since it's a live entropy source. */
n = lles->lles_rsource->les_read(event.he_entropy, HARVESTSIZE);
KASSERT((n > 0 && n <= HARVESTSIZE), ("very bad return from les_read (= %d) in %s", n, __func__));
memset(event.he_entropy + n, 0, HARVESTSIZE - n);
event.he_somecounter = get_cyclecount();
event.he_size = n;
event.he_bits = (n*8)/2;
event.he_source = lles->lles_rsource->les_source;
event.he_destination = harvest_destination[event.he_source]++;
/* Do the actual entropy insertion */
harvest_process_event(&event);
}
}
sx_sunlock(&les_lock);
}
void
live_entropy_sources_init(void)
{
SYSCTL_PROC(_kern_random, OID_AUTO, live_entropy_sources,
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
NULL, 0, live_entropy_source_handler, "",
"List of Active Live Entropy Sources");
sx_init(&les_lock, "live_entropy_sources");
}
void
live_entropy_sources_deinit(void)
{
sx_destroy(&les_lock);
}

View File

@ -1,59 +0,0 @@
/*-
* Copyright (c) 2013 Arthur Mesh <arthurmesh@gmail.com>
* Copyright (c) 2013 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef SYS_DEV_RANDOM_LIVE_ENTROPY_SOURCES_H_INCLUDED
#define SYS_DEV_RANDOM_LIVE_ENTROPY_SOURCES_H_INCLUDED
typedef u_int random_live_read_func_t(void *, u_int);
/*
* Live entropy source is a source of entropy that can provide
* specified or approximate amount of entropy immediately upon request or within
* an acceptable amount of time.
*/
struct live_entropy_source {
const char *les_ident;
enum random_entropy_source les_source;
random_live_read_func_t *les_read;
};
struct live_entropy_sources {
LIST_ENTRY(live_entropy_sources) lles_entries; /* list of providers */
struct live_entropy_source *lles_rsource; /* associated random adaptor */
};
extern struct mtx live_mtx;
void live_entropy_sources_init(void);
void live_entropy_sources_deinit(void);
void live_entropy_source_register(struct live_entropy_source *);
void live_entropy_source_deregister(struct live_entropy_source *);
void live_entropy_sources_feed(void);
#endif /* SYS_DEV_RANDOM_LIVE_ENTROPY_SOURCES_H_INCLUDED */

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2013 Mark R V Murray
* Copyright (c) 2013-2015 Mark R V Murray
* Copyright (c) 2013 David E. O'Brien <obrien@NUXI.org>
* All rights reserved.
*
@ -44,24 +44,17 @@ __FBSDID("$FreeBSD$");
#include <machine/specialreg.h>
#include <dev/random/randomdev.h>
#include <dev/random/randomdev_soft.h>
#include <dev/random/random_adaptors.h>
#include <dev/random/live_entropy_sources.h>
static void random_nehemiah_init(void);
static void random_nehemiah_deinit(void);
static u_int random_nehemiah_read(void *, u_int);
static struct live_entropy_source random_nehemiah = {
.les_ident = "VIA Nehemiah Padlock RNG",
.les_source = RANDOM_PURE_NEHEMIAH,
.les_read = random_nehemiah_read
static struct random_source random_nehemiah = {
.rs_ident = "VIA Nehemiah Padlock RNG",
.rs_source = RANDOM_PURE_NEHEMIAH,
.rs_read = random_nehemiah_read
};
/* XXX: FIX? Now that the Davies-Meyer hash is gone and we only use
* the 'xstore' instruction, do we still need to preserve the
* FPU state with fpu_kern_(enter|leave)() ?
*/
static struct fpu_kern_ctx *fpu_ctx_save;
/* This H/W source never stores more than 8 bytes in one go */
@ -131,8 +124,8 @@ nehemiah_modevent(module_t mod, int type, void *unused)
switch (type) {
case MOD_LOAD:
if (via_feature_rng & VIA_HAS_RNG) {
live_entropy_source_register(&random_nehemiah);
printf("random: live provider: \"%s\"\n", random_nehemiah.les_ident);
random_source_register(&random_nehemiah);
printf("random: fast provider: \"%s\"\n", random_nehemiah.rs_ident);
random_nehemiah_init();
}
break;
@ -140,7 +133,7 @@ nehemiah_modevent(module_t mod, int type, void *unused)
case MOD_UNLOAD:
if (via_feature_rng & VIA_HAS_RNG)
random_nehemiah_deinit();
live_entropy_source_deregister(&random_nehemiah);
random_source_deregister(&random_nehemiah);
break;
case MOD_SHUTDOWN:

View File

@ -1,483 +0,0 @@
/*-
* Copyright (c) 2013 Mark R V Murray
* Copyright (c) 2013 Arthur Mesh <arthurmesh@gmail.com>
* Copyright (c) 2013 David E. O'Brien <obrien@NUXI.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
__FBSDID("$FreeBSD$");
#include "opt_random.h"
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/libkern.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/poll.h>
#include <sys/queue.h>
#include <sys/random.h>
#include <sys/sbuf.h>
#include <sys/selinfo.h>
#include <sys/sx.h>
#include <sys/sysctl.h>
#include <sys/uio.h>
#include <sys/unistd.h>
#include <dev/random/randomdev.h>
#include <dev/random/random_adaptors.h>
#include <dev/random/live_entropy_sources.h>
/* The random_adaptors_lock protects random_adaptors_list and friends and random_adaptor.
* We need a sleepable lock for uiomove/block/poll/sbuf/sysctl.
*/
static struct sx random_adaptors_lock;
LIST_HEAD(adaptors_head, random_adaptors);
static struct adaptors_head random_adaptors_list = LIST_HEAD_INITIALIZER(random_adaptors_list);
static struct random_adaptor *random_adaptor = NULL; /* Currently active adaptor */
/* End of data items requiring random_adaptors_lock protection */
/* The random_readrate_mtx mutex protects the read-rate estimator.
*/
static struct mtx random_read_rate_mtx;
static int random_adaptor_read_rate_cache;
/* End of data items requiring random_readrate_mtx mutex protection */
static struct selinfo rsel;
/* Utility routine to change active adaptor when the random_adaptors_list
* gets modified.
*
* Walk a list of registered random(4) adaptors and pick either a requested
* one or the highest priority one, whichever comes first. Panic on failure
* as the fallback must always be the "dummy" adaptor.
*/
static void
random_adaptor_choose(void)
{
char rngs[128], *token, *cp;
struct random_adaptors *rra, *rrai;
struct random_adaptor *random_adaptor_previous;
int primax;
/* We are going to be messing with random_adaptor.
* Exclusive lock is mandatory.
*/
sx_assert(&random_adaptors_lock, SA_XLOCKED);
random_adaptor_previous = random_adaptor;
random_adaptor = NULL;
if (TUNABLE_STR_FETCH("kern.random.active_adaptor", rngs, sizeof(rngs))) {
cp = rngs;
/* XXX: FIX!! (DES):
* - fetch tunable once, at boot
* - make sysctl r/w
* - when fetching tunable or processing a sysctl
* write, parse into list of strings so we don't
* have to do it here again and again
* - sysctl read should return a reconstructed string
*/
while ((token = strsep(&cp, ",")) != NULL) {
LIST_FOREACH(rra, &random_adaptors_list, rra_entries)
if (strcmp(rra->rra_name, token) == 0) {
random_adaptor = rra->rra_ra;
break;
}
if (random_adaptor != NULL) {
printf("random: selecting requested adaptor <%s>\n",
random_adaptor->ra_ident);
break;
}
else
printf("random: requested adaptor <%s> not available\n",
token);
}
}
primax = 0;
if (random_adaptor == NULL) {
/*
* Fall back to the highest priority item on the available
* RNG list.
*/
LIST_FOREACH(rrai, &random_adaptors_list, rra_entries) {
if (rrai->rra_ra->ra_priority >= primax) {
random_adaptor = rrai->rra_ra;
primax = rrai->rra_ra->ra_priority;
}
}
if (random_adaptor != NULL)
printf("random: selecting highest priority adaptor <%s>\n",
random_adaptor->ra_ident);
}
KASSERT(random_adaptor != NULL, ("adaptor not found"));
/* If we are changing adaptors, deinit the old and init the new. */
if (random_adaptor != random_adaptor_previous) {
#ifdef RANDOM_DEBUG
printf("random: %s - changing from %s to %s\n", __func__,
(random_adaptor_previous == NULL ? "NULL" : random_adaptor_previous->ra_ident),
random_adaptor->ra_ident);
#endif
if (random_adaptor_previous != NULL) {
randomdev_deinit_reader();
(random_adaptor_previous->ra_deinit)();
}
(random_adaptor->ra_init)();
}
randomdev_init_reader(random_adaptor->ra_read);
}
/* XXX: FIX!! Make sure we are not inserting a duplicate */
void
random_adaptor_register(const char *name, struct random_adaptor *ra)
{
struct random_adaptors *rra;
KASSERT(name != NULL && ra != NULL, ("invalid input to %s", __func__));
rra = malloc(sizeof(*rra), M_ENTROPY, M_WAITOK);
rra->rra_name = name;
rra->rra_ra = ra;
sx_xlock(&random_adaptors_lock);
LIST_INSERT_HEAD(&random_adaptors_list, rra, rra_entries);
random_adaptor_choose();
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
sx_xunlock(&random_adaptors_lock);
}
void
random_adaptor_deregister(const char *name)
{
struct random_adaptors *rra;
KASSERT(name != NULL, ("invalid input to %s", __func__));
sx_xlock(&random_adaptors_lock);
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
LIST_FOREACH(rra, &random_adaptors_list, rra_entries)
if (strcmp(rra->rra_name, name) == 0) {
LIST_REMOVE(rra, rra_entries);
break;
}
random_adaptor_choose();
sx_xunlock(&random_adaptors_lock);
free(rra, M_ENTROPY);
}
/* ARGSUSED */
int
random_adaptor_read(struct cdev *dev __unused, struct uio *uio, int flags)
{
void *random_buf;
int c, error;
ssize_t nbytes;
#ifdef RANDOM_DEBUG_VERBOSE
printf("random: %s %ld\n", __func__, uio->uio_resid);
#endif
random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK);
sx_slock(&random_adaptors_lock);
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
/* Let the entropy source do any pre-read setup. */
(random_adaptor->ra_read)(NULL, 0);
/* (Un)Blocking logic */
error = 0;
while (!random_adaptor->ra_seeded() && error == 0) {
if (flags & O_NONBLOCK) {
error = EWOULDBLOCK;
break;
}
/* Sleep instead of going into a spin-frenzy */
error = sx_sleep(&random_adaptor, &random_adaptors_lock,
PUSER | PCATCH, "randrd", hz/10);
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s",
__func__));
/* keep tapping away at the pre-read until we seed/unblock. */
(random_adaptor->ra_read)(NULL, 0);
}
mtx_lock(&random_read_rate_mtx);
/* The read-rate stuff is a rough indication of the instantaneous read rate,
* used to increase the use of 'live' entropy sources when lots of reads are done.
*/
nbytes = (uio->uio_resid + 32 - 1)/32; /* Round up to units of 32 */
random_adaptor_read_rate_cache += nbytes*32;
random_adaptor_read_rate_cache = MIN(random_adaptor_read_rate_cache, 32);
mtx_unlock(&random_read_rate_mtx);
if (error == 0) {
nbytes = uio->uio_resid;
/* The actual read */
while (uio->uio_resid && !error) {
c = MIN(uio->uio_resid, PAGE_SIZE);
(random_adaptor->ra_read)(random_buf, c);
error = uiomove(random_buf, c, uio);
}
/* Let the entropy source do any post-read cleanup. */
(random_adaptor->ra_read)(NULL, 1);
if (nbytes != uio->uio_resid && (error == ERESTART ||
error == EINTR) )
error = 0; /* Return partial read, not error. */
}
sx_sunlock(&random_adaptors_lock);
free(random_buf, M_ENTROPY);
return (error);
}
int
random_adaptor_read_rate(void)
{
int ret;
mtx_lock(&random_read_rate_mtx);
ret = random_adaptor_read_rate_cache;
random_adaptor_read_rate_cache = 1;
mtx_unlock(&random_read_rate_mtx);
return (ret);
}
/* ARGSUSED */
int
random_adaptor_write(struct cdev *dev __unused, struct uio *uio, int flags __unused)
{
int c, error = 0;
void *random_buf;
ssize_t nbytes;
#ifdef RANDOM_DEBUG
printf("random: %s %zd\n", __func__, uio->uio_resid);
#endif
random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK);
sx_slock(&random_adaptors_lock);
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
nbytes = uio->uio_resid;
while (uio->uio_resid > 0 && error == 0) {
c = MIN(uio->uio_resid, PAGE_SIZE);
error = uiomove(random_buf, c, uio);
if (error)
break;
(random_adaptor->ra_write)(random_buf, c);
/* Introduce an annoying delay to stop swamping */
error = sx_sleep(&random_adaptor, &random_adaptors_lock,
PUSER | PCATCH, "randwr", hz/10);
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s",
__func__));
}
sx_sunlock(&random_adaptors_lock);
if (nbytes != uio->uio_resid && (error == ERESTART ||
error == EINTR) )
error = 0; /* Partial write, not error. */
free(random_buf, M_ENTROPY);
return (error);
}
/* ARGSUSED */
int
random_adaptor_poll(struct cdev *dev __unused, int events, struct thread *td __unused)
{
#ifdef RANDOM_DEBUG
printf("random: %s\n", __func__);
#endif
sx_slock(&random_adaptors_lock);
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
if (events & (POLLIN | POLLRDNORM)) {
if (random_adaptor->ra_seeded())
events &= (POLLIN | POLLRDNORM);
else
selrecord(td, &rsel);
}
sx_sunlock(&random_adaptors_lock);
return (events);
}
/* This will be called by the entropy processor when it seeds itself and becomes secure */
void
random_adaptor_unblock(void)
{
selwakeuppri(&rsel, PUSER);
wakeup(&random_adaptor);
printf("random: unblocking device.\n");
/* Do arc4random(9) a favour while we are about it. */
(void)atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_NONE, ARC4_ENTR_HAVE);
}
static int
random_sysctl_adaptors_handler(SYSCTL_HANDLER_ARGS)
{
struct random_adaptors *rra;
struct sbuf sbuf;
int error, count;
sx_slock(&random_adaptors_lock);
sbuf_new_for_sysctl(&sbuf, NULL, 64, req);
count = 0;
LIST_FOREACH(rra, &random_adaptors_list, rra_entries)
sbuf_printf(&sbuf, "%s%s(%d)",
(count++ ? "," : ""), rra->rra_name, rra->rra_ra->ra_priority);
error = sbuf_finish(&sbuf);
sbuf_delete(&sbuf);
sx_sunlock(&random_adaptors_lock);
return (error);
}
static int
random_sysctl_active_adaptor_handler(SYSCTL_HANDLER_ARGS)
{
struct random_adaptors *rra;
struct sbuf sbuf;
int error;
sx_slock(&random_adaptors_lock);
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
sbuf_new_for_sysctl(&sbuf, NULL, 16, req);
LIST_FOREACH(rra, &random_adaptors_list, rra_entries)
if (rra->rra_ra == random_adaptor) {
sbuf_cat(&sbuf, rra->rra_name);
break;
}
error = sbuf_finish(&sbuf);
sbuf_delete(&sbuf);
sx_sunlock(&random_adaptors_lock);
return (error);
}
void
random_adaptors_init(void)
{
#ifdef RANDOM_DEBUG
printf("random: %s\n", __func__);
#endif
SYSCTL_PROC(_kern_random, OID_AUTO, adaptors,
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
NULL, 0, random_sysctl_adaptors_handler, "A",
"Random Number Generator adaptors");
SYSCTL_PROC(_kern_random, OID_AUTO, active_adaptor,
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
NULL, 0, random_sysctl_active_adaptor_handler, "A",
"Active Random Number Generator Adaptor");
sx_init(&random_adaptors_lock, "random_adaptors");
mtx_init(&random_read_rate_mtx, "read rate mutex", NULL, MTX_DEF);
/* The dummy adaptor is not a module by itself, but part of the
* randomdev module.
*/
random_adaptor_register("dummy", &randomdev_dummy);
live_entropy_sources_init();
}
void
random_adaptors_deinit(void)
{
#ifdef RANDOM_DEBUG
printf("random: %s\n", __func__);
#endif
live_entropy_sources_deinit();
/* Don't do this! Panic will surely follow! */
/* random_adaptor_deregister("dummy"); */
mtx_destroy(&random_read_rate_mtx);
sx_destroy(&random_adaptors_lock);
}
/*
* Reseed the active adaptor shortly before starting init(8).
*/
/* ARGSUSED */
static void
random_adaptors_seed(void *unused __unused)
{
sx_slock(&random_adaptors_lock);
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
random_adaptor->ra_reseed();
sx_sunlock(&random_adaptors_lock);
arc4rand(NULL, 0, 1);
}
SYSINIT(random_seed, SI_SUB_KTHREAD_INIT, SI_ORDER_FIRST,
random_adaptors_seed, NULL);

View File

@ -1,74 +0,0 @@
/*-
* Copyright (c) 2013 Arthur Mesh <arthurmesh@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef SYS_DEV_RANDOM_RANDOM_ADAPTORS_H_INCLUDED
#define SYS_DEV_RANDOM_RANDOM_ADAPTORS_H_INCLUDED
MALLOC_DECLARE(M_ENTROPY);
typedef void random_adaptor_init_func_t(void);
typedef void random_adaptor_deinit_func_t(void);
typedef void random_adaptor_read_func_t(uint8_t *, u_int);
typedef void random_adaptor_write_func_t(uint8_t *, u_int);
typedef int random_adaptor_seeded_func_t(void);
typedef void random_adaptor_reseed_func_t(void);
struct random_adaptor {
const char *ra_ident;
int ra_priority;
random_adaptor_init_func_t *ra_init;
random_adaptor_deinit_func_t *ra_deinit;
random_adaptor_read_func_t *ra_read;
random_adaptor_write_func_t *ra_write;
random_adaptor_reseed_func_t *ra_reseed;
random_adaptor_seeded_func_t *ra_seeded;
};
struct random_adaptors {
LIST_ENTRY(random_adaptors) rra_entries; /* list of providers */
const char *rra_name; /* name of random adaptor */
struct random_adaptor *rra_ra;
};
/* Dummy "always-block" pseudo-device */
extern struct random_adaptor randomdev_dummy;
void random_adaptors_init(void);
void random_adaptors_deinit(void);
void random_adaptor_register(const char *, struct random_adaptor *);
void random_adaptor_deregister(const char *);
int random_adaptor_read(struct cdev *, struct uio *, int);
int random_adaptor_write(struct cdev *, struct uio *, int);
int random_adaptor_poll(struct cdev *, int, struct thread *);
int random_adaptor_read_rate(void);
void random_adaptor_unblock(void);
#endif /* SYS_DEV_RANDOM_RANDOM_ADAPTORS_H_INCLUDED */

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2000-2014 Mark R V Murray
* Copyright (c) 2000-2015 Mark R V Murray
* Copyright (c) 2013 Arthur Mesh
* Copyright (c) 2004 Robert N. M. Watson
* All rights reserved.
@ -30,16 +30,17 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_random.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/eventhandler.h>
#include <sys/hash.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/linker.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/random.h>
#include <sys/sbuf.h>
@ -49,9 +50,9 @@ __FBSDID("$FreeBSD$");
#include <machine/cpu.h>
#include <dev/random/randomdev.h>
#include <dev/random/random_adaptors.h>
#include <dev/random/random_harvestq.h>
#include <dev/random/live_entropy_sources.h>
static void random_kthread(void);
/* List for the dynamic sysctls */
static struct sysctl_ctx_list random_clist;
@ -62,122 +63,111 @@ static struct sysctl_ctx_list random_clist;
* supplied junk. When used, they are transferred back to the
* 'empty' queue.
*/
#define RANDOM_FIFO_MAX 1024
#define RANDOM_RING_MAX 1024
#define RANDOM_ACCUM_MAX 8
/* 1 to let the kernel thread run, 0 to terminate */
volatile int random_kthread_control;
/*
* The harvest mutex protects the consistency of the entropy Fifos and
* empty fifo and other associated structures.
* Put all the harvest queue context stuff in one place.
* this make is a bit easier to lock and protect.
*/
static struct mtx harvest_mtx;
static struct harvest_context {
/* The harvest mutex protects the consistency of the entropy Fifos and
* empty fifo and other associated structures.
*/
struct mtx hc_mtx;
/* Round-robin destination cache. */
u_int hc_destination[ENTROPYSOURCE];
/* The context of the kernel thread processing harvested entropy */
struct proc *hc_kthread_proc;
/* Allow the sysadmin to select the broad category of
* entropy types to harvest.
*/
u_int hc_source_mask;
/*
* Lockless ring buffer holding entropy events
* If ring.in == ring.out,
* the buffer is empty.
* If ring.in != ring.out,
* the buffer contains harvested entropy.
* If (ring.in + 1) == ring.out (mod RANDOM_RING_MAX),
* the buffer is full.
*
* The ring.in variable needs locking as there are multiple
* sources to the ring. Only the sources may change ring.in,
* but the consumer may examine it.
*
* The ring.out variable does not need locking as there is
* only one consumer. Only the consumer may change ring.out,
* but the sources may examine it.
*/
struct entropy_ring {
struct harvest_event ring[RANDOM_RING_MAX];
volatile u_int in;
volatile u_int out;
} hc_entropy_ring;
struct fast_entropy_accumulator {
volatile u_int pos;
uint32_t buf[8];
} hc_entropy_fast_accumulator;
} harvest_context;
/*
* Lockable FIFO ring buffer holding entropy events
* If ring_in == ring_out,
* the buffer is empty.
* If (ring_in + 1) == ring_out (MOD RANDOM_FIFO_MAX),
* the buffer is full.
*
* The ring_in variable needs locking as there are multiple
* sources to the ring. Only the sources may change ring_in,
* but the consumer may examine it.
*
* The ring_out variable does not need locking as there is
* only one consumer. Only the consumer may change ring_out,
* but the sources may examine it.
*/
static struct entropyfifo {
struct harvest_event ring[RANDOM_FIFO_MAX];
volatile u_int ring_in;
volatile u_int ring_out;
} entropyfifo;
static struct kproc_desc random_proc_kp = {
"rand_harvestq",
random_kthread,
&harvest_context.hc_kthread_proc,
};
/* Round-robin destination cache. */
u_int harvest_destination[ENTROPYSOURCE];
/* Function called to process one harvested stochastic event */
void (*harvest_process_event)(struct harvest_event *);
/* Allow the sysadmin to select the broad category of
* entropy types to harvest.
*/
static u_int harvest_source_mask = ((1U << RANDOM_ENVIRONMENTAL_END) - 1);
/* Pool count is used by anything needing to know how many entropy
* pools are currently being maintained.
* This is of use to (e.g.) the live source feed where we need to give
* all the pools a top-up.
*/
int harvest_pool_count;
/* <0 to end the kthread, 0 to let it run, 1 to flush the harvest queues */
static int random_kthread_control = 0;
static struct proc *random_kthread_proc;
/* Pass the given event straight through to Fortuna/Yarrow/Whatever. */
static __inline void
random_harvestq_fast_process_event(struct harvest_event *event)
{
if (random_alg_context.ra_event_processor)
random_alg_context.ra_event_processor(event);
}
static void
random_kthread(void *arg __unused)
random_kthread(void)
{
u_int maxloop, ring_out;
u_int maxloop, ring_out, i;
/*
* Process until told to stop.
*
* Locking is not needed as this is the only place we modify ring_out, and
* we only examine ring_in without changing it. Both of these are volatile,
* Locking is not needed as this is the only place we modify ring.out, and
* we only examine ring.in without changing it. Both of these are volatile,
* and this is a unique thread.
*/
while (random_kthread_control >= 0) {
for (random_kthread_control = 1; random_kthread_control;) {
/* Deal with events, if any. Restrict the number we do in one go. */
maxloop = RANDOM_FIFO_MAX;
while (entropyfifo.ring_out != entropyfifo.ring_in) {
ring_out = (entropyfifo.ring_out + 1)%RANDOM_FIFO_MAX;
harvest_process_event(entropyfifo.ring + ring_out);
/* Modifying ring_out here ONLY. Sufficient for atomicity? */
entropyfifo.ring_out = ring_out;
/* The ring may be filled quickly so don't loop forever. */
if (--maxloop)
maxloop = RANDOM_RING_MAX;
while (harvest_context.hc_entropy_ring.out != harvest_context.hc_entropy_ring.in) {
ring_out = (harvest_context.hc_entropy_ring.out + 1)%RANDOM_RING_MAX;
random_harvestq_fast_process_event(harvest_context.hc_entropy_ring.ring + ring_out);
harvest_context.hc_entropy_ring.out = ring_out;
if (!--maxloop)
break;
}
/*
* Give the fast hardware sources a go
*/
live_entropy_sources_feed();
/*
* If a queue flush was commanded, it has now happened,
* and we can mark this by resetting the command.
* A negative value, however, terminates the thread.
*/
if (random_kthread_control == 1)
random_kthread_control = 0;
/* Some work is done, so give the rest of the OS a chance. */
tsleep_sbt(&random_kthread_control, 0, "-", SBT_1S/10, 0, C_PREL(1));
random_sources_feed();
/* XXX: FIX!! This This seems a little slow; 8 items every 0.1s from UMA? */
for (i = 0; i < RANDOM_ACCUM_MAX; i++) {
if (harvest_context.hc_entropy_fast_accumulator.buf[i]) {
random_harvest_direct(harvest_context.hc_entropy_fast_accumulator.buf + i, sizeof(harvest_context.hc_entropy_fast_accumulator.buf[0]), 4, RANDOM_FAST);
harvest_context.hc_entropy_fast_accumulator.buf[i] = 0;
}
}
/* XXX: FIX!! This is a *great* place to pass hardware/live entropy to random(9) */
tsleep_sbt(&harvest_context.hc_kthread_proc, 0, "-", SBT_1S/10, 0, C_PREL(1));
}
randomdev_set_wakeup_exit(&random_kthread_control);
wakeup(&harvest_context.hc_kthread_proc);
kproc_exit(0);
/* NOTREACHED */
}
void
random_harvestq_flush(void)
{
/* Command a entropy queue flush and wait for it to finish */
random_kthread_control = 1;
while (random_kthread_control)
pause("-", hz/10);
}
SYSINIT(random_device_h_proc, SI_SUB_CREATE_INIT, SI_ORDER_ANY, kproc_start, &random_proc_kp);
/* ARGSUSED */
RANDOM_CHECK_UINT(harvestmask, 0, ((1U << RANDOM_ENVIRONMENTAL_END) - 1));
RANDOM_CHECK_UINT(harvestmask, 0, RANDOM_HARVEST_EVERYTHING_MASK);
/* ARGSUSED */
static int
@ -189,12 +179,11 @@ random_print_harvestmask(SYSCTL_HANDLER_ARGS)
error = sysctl_wire_old_buffer(req, 0);
if (error == 0) {
sbuf_new_for_sysctl(&sbuf, NULL, 128, req);
for (i = RANDOM_ENVIRONMENTAL_END - 1; i >= 0; i--)
sbuf_cat(&sbuf, (harvest_source_mask & (1U << i)) ? "1" : "0");
for (i = RANDOM_ENVIRONMENTAL_END; i >= 0; i--)
sbuf_cat(&sbuf, (harvest_context.hc_source_mask & (1 << i)) ? "1" : "0");
error = sbuf_finish(&sbuf);
sbuf_delete(&sbuf);
}
return (error);
}
@ -208,8 +197,8 @@ static const char *(random_source_descr[]) = {
"NET_NG",
"INTERRUPT",
"SWI",
"UMA_ALLOC",
"", /* "ENVIRONMENTAL_END" */
"FS_ATIME",
"HIGH_PERFORMANCE", /* ENVIRONMENTAL_END */
"PURE_OCTEON",
"PURE_SAFE",
"PURE_GLXSB",
@ -231,112 +220,111 @@ random_print_harvestmask_symbolic(SYSCTL_HANDLER_ARGS)
error = sysctl_wire_old_buffer(req, 0);
if (error == 0) {
sbuf_new_for_sysctl(&sbuf, NULL, 128, req);
for (i = RANDOM_ENVIRONMENTAL_END - 1; i >= 0; i--) {
sbuf_cat(&sbuf, (i == RANDOM_ENVIRONMENTAL_END - 1) ? "" : ",");
sbuf_cat(&sbuf, (harvest_source_mask & (1U << i)) ? random_source_descr[i] : "");
for (i = RANDOM_ENVIRONMENTAL_END; i >= 0; i--) {
sbuf_cat(&sbuf, (i == RANDOM_ENVIRONMENTAL_END) ? "" : ",");
sbuf_cat(&sbuf, !(harvest_context.hc_source_mask & (1 << i)) ? "[" : "");
sbuf_cat(&sbuf, random_source_descr[i]);
sbuf_cat(&sbuf, !(harvest_context.hc_source_mask & (1 << i)) ? "]" : "");
}
error = sbuf_finish(&sbuf);
sbuf_delete(&sbuf);
}
return (error);
}
void
random_harvestq_init(void (*event_processor)(struct harvest_event *), int poolcount)
/* ARGSUSED */
static void
random_harvestq_init(void *unused __unused)
{
uint8_t *keyfile, *data;
int error;
size_t size, j;
struct sysctl_oid *random_sys_o;
#ifdef RANDOM_DEBUG
printf("random: %s\n", __func__);
#endif
if (bootverbose)
printf("random: %s\n", __func__);
random_sys_o = SYSCTL_ADD_NODE(&random_clist,
SYSCTL_STATIC_CHILDREN(_kern_random),
OID_AUTO, "harvest", CTLFLAG_RW, 0,
"Entropy Device Parameters");
harvest_context.hc_source_mask = RANDOM_HARVEST_EVERYTHING_MASK;
SYSCTL_ADD_PROC(&random_clist,
SYSCTL_CHILDREN(random_sys_o),
OID_AUTO, "mask", CTLTYPE_UINT | CTLFLAG_RW,
&harvest_source_mask, ((1U << RANDOM_ENVIRONMENTAL_END) - 1),
&harvest_context.hc_source_mask, 0,
random_check_uint_harvestmask, "IU",
"Entropy harvesting mask");
SYSCTL_ADD_PROC(&random_clist,
SYSCTL_CHILDREN(random_sys_o),
OID_AUTO, "mask_bin", CTLTYPE_STRING | CTLFLAG_RD,
NULL, 0, random_print_harvestmask, "A", "Entropy harvesting mask (printable)");
SYSCTL_ADD_PROC(&random_clist,
SYSCTL_CHILDREN(random_sys_o),
OID_AUTO, "mask_symbolic", CTLTYPE_STRING | CTLFLAG_RD,
NULL, 0, random_print_harvestmask_symbolic, "A", "Entropy harvesting mask (symbolic)");
RANDOM_HARVEST_INIT_LOCK();
harvest_context.hc_entropy_ring.in = harvest_context.hc_entropy_ring.out = 0;
}
SYSINIT(random_device_h_init, SI_SUB_RANDOM, SI_ORDER_SECOND, random_harvestq_init, NULL);
/* Point to the correct event_processing function */
harvest_process_event = event_processor;
/*
* This is used to prime the RNG by grabbing any early random stuff
* known to the kernel, and inserting it directly into the hashing
* module, e.g. Fortuna or Yarrow.
*/
/* ARGSUSED */
static void
random_harvestq_prime(void *unused __unused)
{
struct harvest_event event;
size_t count, size, i;
uint8_t *keyfile, *data;
/* Store the pool count (used by live source feed) */
harvest_pool_count = poolcount;
/* Initialise the harvesting mutex and in/out indexes. */
mtx_init(&harvest_mtx, "entropy harvest mutex", NULL, MTX_SPIN);
entropyfifo.ring_in = entropyfifo.ring_out = 0U;
/* Start the hash/reseed thread */
error = kproc_create(random_kthread, NULL,
&random_kthread_proc, RFHIGHPID, 0, "rand_harvestq");
if (error != 0)
panic("Cannot create entropy maintenance thread.");
/* Get entropy that may have been preloaded by loader(8)
/*
* Get entropy that may have been preloaded by loader(8)
* and use it to pre-charge the entropy harvest queue.
*/
keyfile = preload_search_by_type("/boot/entropy");
keyfile = preload_search_by_type(RANDOM_HARVESTQ_BOOT_ENTROPY_FILE);
if (keyfile != NULL) {
data = preload_fetch_addr(keyfile);
size = preload_fetch_size(keyfile);
if (data != NULL && size != 0) {
for (j = 0; j < size; j += 16)
random_harvestq_internal(data + j, 16, 16, RANDOM_CACHED);
printf("random: read %zu bytes from preloaded cache\n", size);
bzero(data, size);
}
else
printf("random: no preloaded entropy cache\n");
for (i = 0; i < size; i += sizeof(event.he_entropy)) {
count = sizeof(event.he_entropy);
event.he_somecounter = (uint32_t)get_cyclecount();
event.he_size = count;
event.he_bits = count/4; /* Underestimate the size for Yarrow */
event.he_source = RANDOM_CACHED;
event.he_destination = harvest_context.hc_destination[0]++;
memcpy(event.he_entropy, data + i, sizeof(event.he_entropy));
random_harvestq_fast_process_event(&event);
explicit_bzero(&event, sizeof(event));
}
explicit_bzero(data, size);
if (bootverbose)
printf("random: read %zu bytes from preloaded cache\n", size);
} else
if (bootverbose)
printf("random: no preloaded entropy cache\n");
}
}
SYSINIT(random_device_prime, SI_SUB_RANDOM, SI_ORDER_FOURTH, random_harvestq_prime, NULL);
void
random_harvestq_deinit(void)
/* ARGSUSED */
static void
random_harvestq_deinit(void *unused __unused)
{
#ifdef RANDOM_DEBUG
printf("random: %s\n", __func__);
#endif
/*
* Command the hash/reseed thread to end and wait for it to finish
*/
random_kthread_control = -1;
tsleep(&random_kthread_control, 0, "term", 0);
mtx_destroy(&harvest_mtx);
/* Command the hash/reseed thread to end and wait for it to finish */
random_kthread_control = 0;
tsleep(&harvest_context.hc_kthread_proc, 0, "term", 0);
sysctl_ctx_free(&random_clist);
}
SYSUNINIT(random_device_h_init, SI_SUB_RANDOM, SI_ORDER_SECOND, random_harvestq_deinit, NULL);
/*
* Entropy harvesting routine.
* This is supposed to be fast; do not do anything slow in here!
/*-
* Entropy harvesting queue routine.
*
* This is supposed to be fast; do not do anything slow in here!
* It is also illegal (and morally reprehensible) to insert any
* high-rate data here. "High-rate" is define as a data source
* high-rate data here. "High-rate" is defined as a data source
* that will usually cause lots of failures of the "Lockless read"
* check a few lines below. This includes the "always-on" sources
* like the Intel "rdrand" or the VIA Nehamiah "xstore" sources.
@ -346,37 +334,78 @@ random_harvestq_deinit(void)
* read which can be quite expensive.
*/
void
random_harvestq_internal(const void *entropy, u_int count, u_int bits,
enum random_entropy_source origin)
random_harvest_queue(const void *entropy, u_int count, u_int bits, enum random_entropy_source origin)
{
struct harvest_event *event;
u_int ring_in;
KASSERT(origin >= RANDOM_START && origin < ENTROPYSOURCE,
("random_harvest_internal: origin %d invalid\n", origin));
/* Mask out unwanted sources */
if (!(harvest_source_mask & (1U << origin)))
KASSERT(origin >= RANDOM_START && origin < ENTROPYSOURCE, ("%s: origin %d invalid\n", __func__, origin));
if (!(harvest_context.hc_source_mask & (1 << origin)))
return;
/* Lock ring_in against multi-thread contention */
mtx_lock_spin(&harvest_mtx);
ring_in = (entropyfifo.ring_in + 1)%RANDOM_FIFO_MAX;
if (ring_in != entropyfifo.ring_out) {
RANDOM_HARVEST_LOCK();
ring_in = (harvest_context.hc_entropy_ring.in + 1)%RANDOM_RING_MAX;
if (ring_in != harvest_context.hc_entropy_ring.out) {
/* The ring is not full */
event = entropyfifo.ring + ring_in;
/* Stash the harvested stuff in the *event buffer */
count = MIN(count, HARVESTSIZE);
event->he_somecounter = get_cyclecount();
event->he_size = count;
event->he_bits = bits;
event = harvest_context.hc_entropy_ring.ring + ring_in;
event->he_somecounter = (uint32_t)get_cyclecount();
event->he_source = origin;
event->he_destination = harvest_destination[origin]++;
memcpy(event->he_entropy, entropy, count);
memset(event->he_entropy + count, 0, HARVESTSIZE - count);
entropyfifo.ring_in = ring_in;
event->he_destination = harvest_context.hc_destination[origin]++;
event->he_bits = bits;
if (count <= sizeof(event->he_entropy)) {
event->he_size = count;
memcpy(event->he_entropy, entropy, count);
}
else {
/* Big event, so squash it */
event->he_size = sizeof(event->he_entropy[0]);
event->he_entropy[0] = jenkins_hash(entropy, count, (uint32_t)(uintptr_t)event);
}
harvest_context.hc_entropy_ring.in = ring_in;
}
mtx_unlock_spin(&harvest_mtx);
RANDOM_HARVEST_UNLOCK();
}
/*-
* Entropy harvesting fast routine.
*
* This is supposed to be very fast; do not do anything slow in here!
* This is the right place for high-rate harvested data.
*/
void
random_harvest_fast(const void *entropy, u_int count, u_int bits, enum random_entropy_source origin)
{
u_int pos;
KASSERT(origin >= RANDOM_START && origin < ENTROPYSOURCE, ("%s: origin %d invalid\n", __func__, origin));
/* XXX: FIX!! The above KASSERT is BS. Right now we ignore most structure and just accumulate the supplied data */
if (!(harvest_context.hc_source_mask & (1 << origin)))
return;
pos = harvest_context.hc_entropy_fast_accumulator.pos;
harvest_context.hc_entropy_fast_accumulator.buf[pos] ^= jenkins_hash(entropy, count, (uint32_t)get_cyclecount());
harvest_context.hc_entropy_fast_accumulator.pos = (pos + 1)%RANDOM_ACCUM_MAX;
}
/*-
* Entropy harvesting direct routine.
*
* This is not supposed to be fast, but will only be used during
* (e.g.) booting when initial entropy is being gathered.
*/
void
random_harvest_direct(const void *entropy, u_int count, u_int bits, enum random_entropy_source origin)
{
struct harvest_event event;
KASSERT(origin >= RANDOM_START && origin < ENTROPYSOURCE, ("%s: origin %d invalid\n", __func__, origin));
if (!(harvest_context.hc_source_mask & (1 << origin)))
return;
count = MIN(count, sizeof(event.he_entropy));
event.he_somecounter = (uint32_t)get_cyclecount();
event.he_size = count;
event.he_bits = bits;
event.he_source = origin;
event.he_destination = harvest_context.hc_destination[origin]++;
memcpy(event.he_entropy, entropy, count);
random_harvestq_fast_process_event(&event);
explicit_bzero(&event, sizeof(event));
}

View File

@ -1,6 +1,5 @@
/*-
* Copyright (c) 2013-2014 Mark R V Murray
* Copyright (c) 2013 Arthur Mesh <arthurmesh@gmail.com>
* Copyright (c) 2013-2015 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -28,43 +27,26 @@
*/
#ifndef SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED
#define SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED
#define SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED
#define HARVESTSIZE 16 /* max size of each harvested entropy unit */
#define HARVESTSIZE 2 /* Max length in words of each harvested entropy unit */
/* These are used to queue harvested packets of entropy. The entropy
* buffer size is pretty arbitrary.
*/
struct harvest_event {
uintmax_t he_somecounter; /* fast counter for clock jitter */
uint8_t he_entropy[HARVESTSIZE];/* some harvested entropy */
u_int he_size; /* harvested entropy byte count */
u_int he_bits; /* stats about the entropy */
u_int he_destination; /* destination pool of this entropy */
enum random_entropy_source he_source; /* origin of the entropy */
};
uint32_t he_somecounter; /* fast counter for clock jitter */
uint32_t he_entropy[HARVESTSIZE];/* some harvested entropy */
uint8_t he_size; /* harvested entropy byte count */
uint8_t he_bits; /* stats about the entropy */
uint8_t he_destination; /* destination pool of this entropy */
uint8_t he_source; /* origin of the entropy */
} __packed;
void random_harvestq_init(void (*)(struct harvest_event *), int);
void random_harvestq_deinit(void);
void random_harvestq_internal(const void *, u_int, u_int, enum random_entropy_source);
#define RANDOM_HARVESTQ_BOOT_ENTROPY_FILE "/boot/entropy"
/* Pool count is used by anything needing to know how many entropy
* pools are currently being maintained.
* This is of use to (e.g.) the live source feed where we need to give
* all the pools a top-up.
*/
extern int harvest_pool_count;
/* This is in randomdev.c as it needs to be permanently in the kernel */
void randomdev_set_wakeup_exit(void *);
/* Force all currently pending queue contents to clear, and kick the software processor */
void random_harvestq_flush(void);
/* Function called to process one harvested stochastic event */
extern void (*harvest_process_event)(struct harvest_event *);
/* Round-robin destination cache. */
extern u_int harvest_destination[ENTROPYSOURCE];
#define RANDOM_HARVEST_INIT_LOCK(x) mtx_init(&harvest_context.hc_mtx, "entropy harvest mutex", NULL, MTX_SPIN)
#define RANDOM_HARVEST_LOCK(x) mtx_lock_spin(&harvest_context.hc_mtx)
#define RANDOM_HARVEST_UNLOCK(x) mtx_unlock_spin(&harvest_context.hc_mtx)
#endif /* SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED */

View File

@ -1,6 +1,5 @@
/*-
* Copyright (c) 2000-2013 Mark R V Murray
* Copyright (c) 2013 Arthur Mesh <arthurmesh@gmail.com>
* Copyright (c) 2000-2015 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -26,28 +25,9 @@
*
*/
/*
* NOTE NOTE NOTE
*
* This file is compiled into the kernel unconditionally. Any random(4)
* infrastructure that needs to be in the kernel by default goes here!
*
* Except ...
*
* The adaptor code all goes into random_adaptor.c, which is also compiled
* the kernel by default. The module in that file is initialised before
* this one.
*
* Other modules must be initialised after the above two, and are
* software random processors which plug into random_adaptor.c.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_random.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
@ -59,27 +39,42 @@ __FBSDID("$FreeBSD$");
#include <sys/lock.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/poll.h>
#include <sys/proc.h>
#include <sys/random.h>
#include <sys/sbuf.h>
#include <sys/selinfo.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/unistd.h>
#include <crypto/rijndael/rijndael-api-fst.h>
#include <crypto/sha2/sha2.h>
#include <dev/random/hash.h>
#include <dev/random/randomdev.h>
#include <dev/random/random_adaptors.h>
#include <dev/random/random_harvestq.h>
#define RANDOM_MINOR 0
#include "opt_random.h"
#if defined(RANDOM_DUMMY) && defined(RANDOM_YARROW)
#error "Cannot define both RANDOM_DUMMY and RANDOM_YARROW"
#endif
#define RANDOM_MINOR 0
static d_read_t randomdev_read;
static d_write_t randomdev_write;
static d_poll_t randomdev_poll;
static d_ioctl_t randomdev_ioctl;
static struct cdevsw random_cdevsw = {
.d_name = "random",
.d_version = D_VERSION,
.d_read = random_adaptor_read,
.d_write = random_adaptor_write,
.d_poll = random_adaptor_poll,
.d_read = randomdev_read,
.d_write = randomdev_write,
.d_poll = randomdev_poll,
.d_ioctl = randomdev_ioctl,
};
@ -87,10 +82,183 @@ static struct cdevsw random_cdevsw = {
static struct cdev *random_dev;
/* Set up the sysctl root node for the entropy device */
SYSCTL_NODE(_kern, OID_AUTO, random, CTLFLAG_RW, 0, "Random Number Generator");
SYSCTL_NODE(_kern, OID_AUTO, random, CTLFLAG_RW, 0, "Cryptographically Secure Random Number Generator");
MALLOC_DEFINE(M_ENTROPY, "entropy", "Entropy harvesting buffers and data structures");
#if defined(RANDOM_DUMMY)
/*-
* Dummy "always block" pseudo algorithm, used when there is no real
* random(4) driver to provide a CSPRNG.
*/
static u_int
dummy_random_zero(void)
{
return (0);
}
static void
dummy_random(void)
{
}
struct random_algorithm random_alg_context = {
.ra_ident = "Dummy",
.ra_reseed = dummy_random,
.ra_seeded = (random_alg_seeded_t *)dummy_random_zero,
.ra_pre_read = dummy_random,
.ra_read = (random_alg_read_t *)dummy_random_zero,
.ra_post_read = dummy_random,
.ra_write = (random_alg_write_t *)dummy_random_zero,
.ra_event_processor = NULL,
.ra_poolcount = 0,
};
#else /* !defined(RANDOM_DUMMY) */
LIST_HEAD(sources_head, random_sources);
static struct sources_head source_list = LIST_HEAD_INITIALIZER(source_list);
static u_int read_rate;
#endif /* defined(RANDOM_DUMMY) */
static struct selinfo rsel;
/*
* This is the read uio(9) interface for random(4).
*/
/* ARGSUSED */
static int
randomdev_read(struct cdev *dev __unused, struct uio *uio, int flags)
{
uint8_t *random_buf;
int c, error;
ssize_t nbytes;
random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK);
random_alg_context.ra_pre_read();
/* (Un)Blocking logic */
error = 0;
while (!random_alg_context.ra_seeded() && error == 0) {
if (flags & O_NONBLOCK) {
error = EWOULDBLOCK;
break;
}
tsleep(&random_alg_context, 0, "randrd", hz/10);
/* keep tapping away at the pre-read until we seed/unblock. */
random_alg_context.ra_pre_read();
printf("random: %s unblock (error = %d)\n", __func__, error);
}
if (error == 0) {
#if !defined(RANDOM_DUMMY)
/* XXX: FIX!! Next line as an atomic operation? */
read_rate += (uio->uio_resid + sizeof(uint32_t))/sizeof(uint32_t);
#endif
nbytes = uio->uio_resid;
while (uio->uio_resid && !error) {
c = MIN(uio->uio_resid, PAGE_SIZE);
random_alg_context.ra_read(random_buf, c);
error = uiomove(random_buf, c, uio);
}
random_alg_context.ra_post_read();
if (nbytes != uio->uio_resid && (error == ERESTART || error == EINTR) )
/* Return partial read, not error. */
error = 0;
}
free(random_buf, M_ENTROPY);
return (error);
}
/*-
* Kernel API version of read_random().
* This is similar to random_alg_read(),
* except it doesn't interface with uio(9).
* It cannot assumed that random_buf is a multiple of
* RANDOM_BLOCKSIZE bytes.
*/
u_int
read_random(void *random_buf, u_int len)
{
u_int read_len, total_read, c;
uint8_t local_buf[len + RANDOM_BLOCKSIZE];
KASSERT(random_buf != NULL, ("No suitable random buffer in %s", __func__));
random_alg_context.ra_pre_read();
/* (Un)Blocking logic; if not seeded, return nothing. */
if (random_alg_context.ra_seeded()) {
#if !defined(RANDOM_DUMMY)
/* XXX: FIX!! Next line as an atomic operation? */
read_rate += (len + sizeof(uint32_t))/sizeof(uint32_t);
#endif
read_len = len;
total_read = 0;
while (read_len) {
c = MIN(read_len, PAGE_SIZE);
random_alg_context.ra_read(&local_buf[total_read], c);
read_len -= c;
total_read += c;
}
memcpy(random_buf, local_buf, len);
} else
len = 0;
random_alg_context.ra_post_read();
return (len);
}
/* ARGSUSED */
static int
randomdev_write(struct cdev *dev __unused, struct uio *uio, int flags __unused)
{
uint8_t *random_buf;
int c, error = 0;
ssize_t nbytes;
random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK);
nbytes = uio->uio_resid;
while (uio->uio_resid > 0 && error == 0) {
c = MIN(uio->uio_resid, PAGE_SIZE);
error = uiomove(random_buf, c, uio);
if (error)
break;
random_alg_context.ra_write(random_buf, c);
tsleep(&random_alg_context, 0, "randwr", hz/10);
}
if (nbytes != uio->uio_resid && (error == ERESTART || error == EINTR))
/* Partial write, not error. */
error = 0;
free(random_buf, M_ENTROPY);
return (error);
}
/* ARGSUSED */
static int
randomdev_poll(struct cdev *dev __unused, int events, struct thread *td __unused)
{
if (events & (POLLIN | POLLRDNORM)) {
if (random_alg_context.ra_seeded())
events &= (POLLIN | POLLRDNORM);
else
selrecord(td, &rsel);
}
return (events);
}
/* This will be called by the entropy processor when it seeds itself and becomes secure */
void
randomdev_unblock(void)
{
selwakeuppri(&rsel, PUSER);
wakeup(&random_alg_context);
printf("random: unblocking device.\n");
/* Do random(9) a favour while we are about it. */
(void)atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_NONE, ARC4_ENTR_HAVE);
}
/* ARGSUSED */
static int
randomdev_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr __unused,
@ -103,30 +271,107 @@ randomdev_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr __unused,
case FIOASYNC:
case FIONBIO:
break;
default:
error = ENOTTY;
}
return (error);
}
/* Helper routine to enable kproc_exit() to work while the module is
* being (or has been) unloaded.
* This routine is in this file because it is always linked into the kernel,
* and will thus never be unloaded. This is critical for unloadable modules
* that have threads.
void
random_source_register(struct random_source *rsource)
{
#if defined(RANDOM_DUMMY)
(void)rsource;
#else /* !defined(RANDOM_DUMMY) */
struct random_sources *rrs;
KASSERT(rsource != NULL, ("invalid input to %s", __func__));
rrs = malloc(sizeof(*rrs), M_ENTROPY, M_WAITOK);
rrs->rrs_source = rsource;
printf("random: registering fast source %s\n", rsource->rs_ident);
LIST_INSERT_HEAD(&source_list, rrs, rrs_entries);
#endif /* defined(RANDOM_DUMMY) */
}
void
random_source_deregister(struct random_source *rsource)
{
#if defined(RANDOM_DUMMY)
(void)rsource;
#else /* !defined(RANDOM_DUMMY) */
struct random_sources *rrs = NULL;
KASSERT(rsource != NULL, ("invalid input to %s", __func__));
LIST_FOREACH(rrs, &source_list, rrs_entries)
if (rrs->rrs_source == rsource) {
LIST_REMOVE(rrs, rrs_entries);
break;
}
if (rrs != NULL)
free(rrs, M_ENTROPY);
#endif /* defined(RANDOM_DUMMY) */
}
#if !defined(RANDOM_DUMMY)
/*
* Run through all fast sources reading entropy for the given
* number of rounds, which should be a multiple of the number
* of entropy accumulation pools in use; 2 for Yarrow and 32
* for Fortuna.
*
* BEWARE!!!
* This function runs inside the RNG thread! Don't do anything silly!
*/
void
randomdev_set_wakeup_exit(void *control)
random_sources_feed(void)
{
uint32_t entropy[HARVESTSIZE];
struct random_sources *rrs;
u_int i, n, local_read_rate;
wakeup(control);
kproc_exit(0);
/* NOTREACHED */
/*
* Step over all of live entropy sources, and feed their output
* to the system-wide RNG.
*/
/* XXX: FIX!! Next lines as an atomic operation? */
local_read_rate = read_rate;
read_rate = RANDOM_ALG_READ_RATE_MINIMUM;
LIST_FOREACH(rrs, &source_list, rrs_entries) {
for (i = 0; i < random_alg_context.ra_poolcount*local_read_rate; i++) {
n = rrs->rrs_source->rs_read(entropy, sizeof(entropy));
KASSERT((n > 0 && n <= sizeof(entropy)), ("very bad return from rs_read (= %d) in %s", n, __func__));
random_harvest_direct(entropy, n, (n*8)/2, rrs->rrs_source->rs_source);
}
}
explicit_bzero(entropy, sizeof(entropy));
}
static int
random_source_handler(SYSCTL_HANDLER_ARGS)
{
struct random_sources *rrs;
struct sbuf sbuf;
int error, count;
sbuf_new_for_sysctl(&sbuf, NULL, 64, req);
count = 0;
LIST_FOREACH(rrs, &source_list, rrs_entries) {
sbuf_cat(&sbuf, (count++ ? ",'" : "'"));
sbuf_cat(&sbuf, rrs->rrs_source->rs_ident);
sbuf_cat(&sbuf, "'");
}
error = sbuf_finish(&sbuf);
sbuf_delete(&sbuf);
return (error);
}
SYSCTL_PROC(_kern_random, OID_AUTO, random_sources, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
NULL, 0, random_source_handler, "A",
"List of active fast entropy sources.");
#endif /* !defined(RANDOM_DUMMY) */
/* ARGSUSED */
static int
randomdev_modevent(module_t mod __unused, int type, void *data __unused)
@ -135,115 +380,28 @@ randomdev_modevent(module_t mod __unused, int type, void *data __unused)
switch (type) {
case MOD_LOAD:
printf("random: entropy device infrastructure driver\n");
printf("random: entropy device external interface\n");
random_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &random_cdevsw,
RANDOM_MINOR, NULL, UID_ROOT, GID_WHEEL, 0644, "random");
make_dev_alias(random_dev, "urandom"); /* compatibility */
random_adaptors_init();
break;
case MOD_UNLOAD:
random_adaptors_deinit();
destroy_dev(random_dev);
break;
case MOD_SHUTDOWN:
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
DEV_MODULE_ORDERED(randomdev, randomdev_modevent, NULL, SI_ORDER_SECOND);
MODULE_VERSION(randomdev, 1);
static moduledata_t randomdev_mod = {
"random_device",
randomdev_modevent,
0
};
/* ================
* Harvesting stubs
* ================
*/
/* Internal stub/fake routine for when no entropy processor is loaded.
* If the entropy device is not loaded, don't act on harvesting calls
* and just return.
*/
/* ARGSUSED */
static void
random_harvest_phony(const void *entropy __unused, u_int count __unused,
u_int bits __unused, enum random_entropy_source origin __unused)
{
}
/* Hold the address of the routine which is actually called */
static void (*reap_func)(const void *, u_int, u_int, enum random_entropy_source) = random_harvest_phony;
/* Initialise the harvester when/if it is loaded */
void
randomdev_init_harvester(void (*reaper)(const void *, u_int, u_int, enum random_entropy_source))
{
reap_func = reaper;
}
/* Deinitialise the harvester when/if it is unloaded */
void
randomdev_deinit_harvester(void)
{
reap_func = random_harvest_phony;
}
/* Entropy harvesting routine.
* Implemented as in indirect call to allow non-inclusion of
* the entropy device.
*/
void
random_harvest(const void *entropy, u_int count, u_int bits, enum random_entropy_source origin)
{
(*reap_func)(entropy, count, bits, origin);
}
/* ================================
* Internal reading stubs and fakes
* ================================
*/
/* Hold the address of the routine which is actually called */
static void (*read_func)(uint8_t *, u_int) = dummy_random_read_phony;
/* Initialise the reader when/if it is loaded */
void
randomdev_init_reader(void (*reader)(uint8_t *, u_int))
{
read_func = reader;
}
/* Deinitialise the reader when/if it is unloaded */
void
randomdev_deinit_reader(void)
{
read_func = dummy_random_read_phony;
}
/* Kernel API version of read_random().
* Implemented as in indirect call to allow non-inclusion of
* the entropy device.
*/
int
read_random(void *buf, int count)
{
if (count < 0)
return 0;
read_func(buf, count);
return count;
}
DECLARE_MODULE(random_device, randomdev_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
MODULE_VERSION(random_device, 1);

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2000-2013 Mark R V Murray
* Copyright (c) 2000-2015 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -27,41 +27,87 @@
*/
#ifndef SYS_DEV_RANDOM_RANDOMDEV_H_INCLUDED
#define SYS_DEV_RANDOM_RANDOMDEV_H_INCLUDED
#define SYS_DEV_RANDOM_RANDOMDEV_H_INCLUDED
/* This header contains only those definitions that are global
* and non algorithm-specific for the entropy processor
*/
typedef void random_init_func_t(void);
typedef void random_deinit_func_t(void);
void randomdev_init_harvester(void (*)(const void *, u_int, u_int, enum random_entropy_source));
void randomdev_init_reader(void (*)(uint8_t *, u_int));
void randomdev_deinit_harvester(void);
void randomdev_deinit_reader(void);
/* Stub/fake routines for when no entropy processor is loaded */
extern void dummy_random_read_phony(uint8_t *, u_int);
/* kern.random sysctls */
#ifdef SYSCTL_DECL /* from sysctl.h */
SYSCTL_DECL(_kern_random);
/* If this was C++, the macro below would be a template */
#define RANDOM_CHECK_UINT(name, min, max) \
#define RANDOM_CHECK_UINT(name, min, max) \
static int \
random_check_uint_##name(SYSCTL_HANDLER_ARGS) \
{ \
if (oidp->oid_arg1 != NULL) { \
if (*(u_int *)(oidp->oid_arg1) <= (min)) \
if (*(u_int *)(oidp->oid_arg1) <= (min)) \
*(u_int *)(oidp->oid_arg1) = (min); \
else if (*(u_int *)(oidp->oid_arg1) > (max)) \
else if (*(u_int *)(oidp->oid_arg1) > (max)) \
*(u_int *)(oidp->oid_arg1) = (max); \
} \
return (sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, \
return (sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, \
req)); \
}
#endif /* SYSCTL_DECL */
#endif
MALLOC_DECLARE(M_ENTROPY);
#define RANDOM_ALG_READ_RATE_MINIMUM 32
struct harvest_event;
typedef void random_alg_pre_read_t(void);
typedef void random_alg_read_t(uint8_t *, u_int);
typedef void random_alg_post_read_t(void);
typedef void random_alg_write_t(uint8_t *, u_int);
typedef int random_alg_seeded_t(void);
typedef void random_alg_reseed_t(void);
typedef void random_alg_eventprocessor_t(struct harvest_event *);
typedef u_int random_source_read_t(void *, u_int);
/*
* Random Algorithm is a processor of randomness for the kernel
* and for userland.
*/
struct random_algorithm {
const char *ra_ident;
u_int ra_poolcount;
random_alg_pre_read_t *ra_pre_read;
random_alg_read_t *ra_read;
random_alg_post_read_t *ra_post_read;
random_alg_write_t *ra_write;
random_alg_reseed_t *ra_reseed;
random_alg_seeded_t *ra_seeded;
random_alg_eventprocessor_t *ra_event_processor;
};
extern struct random_algorithm random_alg_context;
/*
* Random Source is a source of entropy that can provide
* specified or approximate amount of entropy immediately
* upon request.
*/
struct random_source {
const char *rs_ident;
enum random_entropy_source rs_source;
random_source_read_t *rs_read;
};
#if !defined(RANDOM_DUMMY)
struct random_sources {
LIST_ENTRY(random_sources) rrs_entries;
struct random_source *rrs_source;
};
#endif /* !defined(RANDOM_DUMMY) */
void random_source_register(struct random_source *);
void random_source_deregister(struct random_source *);
void random_sources_feed(void);
void randomdev_unblock(void);
#endif /* SYS_DEV_RANDOM_RANDOMDEV_H_INCLUDED */

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2000-2013 Mark R V Murray
* Copyright (c) 2015 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -23,17 +23,50 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef SYS_DEV_RANDOM_RANDOMDEV_SOFT_H_INCLUDED
#define SYS_DEV_RANDOM_RANDOMDEV_SOFT_H_INCLUDED
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/* This header contains only those definitions that are
* specific to the entropy processor
*/
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/random.h>
#include <sys/systm.h>
void randomdev_init(void);
void randomdev_deinit(void);
#include <dev/random/randomdev.h>
#include "opt_random.h"
#if defined(RANDOM_DUMMY) || defined(RANDOM_YARROW)
#error "Cannot define any of RANDOM_DUMMY and RANDOM_YARROW without 'device random'"
#endif
/*-
* Dummy "not even here" device. Stub out all routines that the kernel would need.
*/
/* ARGSUSED */
u_int
read_random(void *random_buf __unused, u_int len __unused)
{
return (0);
}
/* ARGSUSED */
void
random_harvest_direct(const void *entropy __unused, u_int count __unused, u_int bits __unused, enum random_entropy_source origin __unused)
{
}
/* ARGSUSED */
void
random_harvest_queue(const void *entropy __unused, u_int count __unused, u_int bits __unused, enum random_entropy_source origin __unused)
{
}
/* ARGSUSED */
void
random_harvest_fast(const void *entropy __unused, u_int count __unused, u_int bits __unused, enum random_entropy_source origin __unused)
{
}

View File

@ -1,165 +0,0 @@
/*-
* Copyright (c) 2000-2014 Mark R V Murray
* Copyright (c) 2004 Robert N. M. Watson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
* This is the loadable infrastructure base file for software CSPRNG
* drivers such as Yarrow or Fortuna.
*
* It is anticipated that one instance of this file will be used
* for _each_ invocation of a CSPRNG, but with different #defines
* set. See below.
*
*/
#include "opt_random.h"
#if !defined(RANDOM_YARROW) && !defined(RANDOM_FORTUNA)
#define RANDOM_YARROW
#elif defined(RANDOM_YARROW) && defined(RANDOM_FORTUNA)
#error "Must define either RANDOM_YARROW or RANDOM_FORTUNA"
#endif
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/poll.h>
#include <sys/random.h>
#include <sys/sysctl.h>
#include <sys/unistd.h>
#include <dev/random/randomdev.h>
#include <dev/random/randomdev_soft.h>
#include <dev/random/random_harvestq.h>
#include <dev/random/random_adaptors.h>
#if defined(RANDOM_YARROW)
#include <dev/random/yarrow.h>
#endif
#if defined(RANDOM_FORTUNA)
#include <dev/random/fortuna.h>
#endif
static struct random_adaptor random_soft_processor = {
#if defined(RANDOM_YARROW)
#define RANDOM_CSPRNG_NAME "yarrow"
.ra_ident = "Yarrow",
.ra_priority = 90, /* High priority, so top of the list. Fortuna may still win. */
.ra_read = random_yarrow_read,
.ra_write = random_yarrow_write,
.ra_reseed = random_yarrow_reseed,
.ra_seeded = random_yarrow_seeded,
#endif
#if defined(RANDOM_FORTUNA)
#define RANDOM_CSPRNG_NAME "fortuna"
.ra_ident = "Fortuna",
.ra_priority = 100, /* High priority, so top of the list. Beat Yarrow. */
.ra_read = random_fortuna_read,
.ra_write = random_fortuna_write,
.ra_reseed = random_fortuna_reseed,
.ra_seeded = random_fortuna_seeded,
#endif
.ra_init = randomdev_init,
.ra_deinit = randomdev_deinit,
};
void
randomdev_init(void)
{
#if defined(RANDOM_YARROW)
random_yarrow_init_alg();
random_harvestq_init(random_yarrow_process_event, 2);
#endif
#if defined(RANDOM_FORTUNA)
random_fortuna_init_alg();
random_harvestq_init(random_fortuna_process_event, 32);
#endif
/* Register the randomness harvesting routine */
randomdev_init_harvester(random_harvestq_internal);
}
void
randomdev_deinit(void)
{
/* Deregister the randomness harvesting routine */
randomdev_deinit_harvester();
#if defined(RANDOM_YARROW)
random_yarrow_deinit_alg();
#endif
#if defined(RANDOM_FORTUNA)
random_fortuna_deinit_alg();
#endif
}
/* ARGSUSED */
static int
randomdev_soft_modevent(module_t mod __unused, int type, void *unused __unused)
{
int error = 0;
switch (type) {
case MOD_LOAD:
printf("random: SOFT: %s init()\n", RANDOM_CSPRNG_NAME);
random_adaptor_register(RANDOM_CSPRNG_NAME, &random_soft_processor);
break;
case MOD_UNLOAD:
random_adaptor_deregister(RANDOM_CSPRNG_NAME);
break;
case MOD_SHUTDOWN:
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
#if defined(RANDOM_YARROW)
DEV_MODULE(yarrow, randomdev_soft_modevent, NULL);
MODULE_VERSION(yarrow, 1);
MODULE_DEPEND(yarrow, randomdev, 1, 1, 1);
#endif
#if defined(RANDOM_FORTUNA)
DEV_MODULE(fortuna, randomdev_soft_modevent, NULL);
MODULE_VERSION(fortuna, 1);
MODULE_DEPEND(fortuna, randomdev, 1, 1, 1);
#endif

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2014 Mark R V Murray
* Copyright (c) 2015 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -27,7 +27,7 @@
*/
#ifndef SYS_DEV_RANDOM_UINT128_H_INCLUDED
#define SYS_DEV_RANDOM_UINT128_H_INCLUDED
#define SYS_DEV_RANDOM_UINT128_H_INCLUDED
/* This whole thing is a crock :-(
*
@ -35,40 +35,41 @@
*/
#ifdef __SIZEOF_INT128__
#define USE_REAL_UINT128_T
#endif
#ifdef USE_REAL_UINT128_T
typedef __uint128_t uint128_t;
#define UINT128_ZERO 0ULL
#else
typedef uint64_t uint128_t[2];
typedef struct {
/* Ignore endianness */
uint64_t u128t_word0;
uint64_t u128t_word1;
} uint128_t;
static const uint128_t very_long_zero = {0UL,0UL};
#define UINT128_ZERO very_long_zero
#endif
static __inline void
uint128_clear(uint128_t *big_uint)
uint128_increment(uint128_t *big_uintp)
{
#ifdef __SIZEOF_INT128__
(*big_uint) = 0ULL;
#ifdef USE_REAL_UINT128_T
(*big_uintp)++;
#else
(*big_uint)[0] = (*big_uint)[1] = 0UL;
#endif
}
static __inline void
uint128_increment(uint128_t *big_uint)
{
#ifdef __SIZEOF_INT128__
(*big_uint)++;
#else
(*big_uint)[0]++;
if ((*big_uint)[0] == 0UL)
(*big_uint)[1]++;
big_uintp->u128t_word0++;
if (big_uintp->u128t_word0 == 0UL)
big_uintp->u128t_word1++;
#endif
}
static __inline int
uint128_is_zero(uint128_t big_uint)
{
#ifdef __SIZEOF_INT128__
return (big_uint == 0ULL);
#ifdef USE_REAL_UINT128_T
return (big_uint == UINT128_ZERO);
#else
return (big_uint[0] == 0UL && big_uint[1] == 0UL);
return (big_uint.u128t_word0 == 0UL && big_uint.u128t_word1 == 0UL);
#endif
}

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2000-2013 Mark R V Murray
* Copyright (c) 2000-2015 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -37,6 +37,7 @@ cc -g -O0 -pthread -DRANDOM_<alg> -DRANDOM_DEBUG -I../.. -lstdthreads -Wall \
../../crypto/rijndael/rijndael-api-fst.c \
../../crypto/rijndael/rijndael-alg-fst.c \
../../crypto/sha2/sha2.c \
-lz \
-o unit_test
./unit_test
@ -49,6 +50,7 @@ Where <alg> is YARROW or FORTUNA.
#include <stdlib.h>
#include <threads.h>
#include <unistd.h>
#include <zlib.h>
#include "unit_test.h"
@ -59,10 +61,77 @@ Where <alg> is YARROW or FORTUNA.
#include "dev/random/fortuna.h"
#endif
#define NUM_THREADS 3
#define NUM_THREADS 3
#define DEBUG
static volatile int stopseeding = 0;
static __inline void
check_err(int err, const char *func)
{
if (err != Z_OK) {
fprintf(stderr, "Compress error in %s: %d\n", func, err);
exit(0);
}
}
void *
myalloc(void *q, unsigned n, unsigned m)
{
q = Z_NULL;
return (calloc(n, m));
}
void myfree(void *q, void *p)
{
q = Z_NULL;
free(p);
}
size_t
block_deflate(uint8_t *uncompr, uint8_t *compr, const size_t len)
{
z_stream c_stream;
int err;
if (len == 0)
return (0);
c_stream.zalloc = myalloc;
c_stream.zfree = myfree;
c_stream.opaque = NULL;
err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
check_err(err, "deflateInit");
c_stream.next_in = uncompr;
c_stream.next_out = compr;
c_stream.avail_in = len;
c_stream.avail_out = len*2u +512u;
while (c_stream.total_in != len && c_stream.total_out < (len*2u + 512u)) {
err = deflate(&c_stream, Z_NO_FLUSH);
#ifdef DEBUG
printf("deflate: len = %zd total_in = %lu total_out = %lu\n", len, c_stream.total_in, c_stream.total_out);
#endif
check_err(err, "deflate(..., Z_NO_FLUSH)");
}
for (;;) {
err = deflate(&c_stream, Z_FINISH);
#ifdef DEBUG
printf("deflate: len = %zd total_in = %lu total_out = %lu\n", len, c_stream.total_in, c_stream.total_out);
#endif
if (err == Z_STREAM_END) break;
check_err(err, "deflate(..., Z_STREAM_END)");
}
err = deflateEnd(&c_stream);
check_err(err, "deflateEnd");
return ((size_t)c_stream.total_out);
}
void
random_adaptor_unblock(void)
{
@ -128,6 +197,7 @@ WriteCSPRNG(void *threadid)
if (i % 1000 == 0)
printf("Thread write 1 - %d\n", i);
if (buf != NULL) {
printf("Thread 1 writing.\n");
#ifdef RANDOM_YARROW
random_yarrow_write(buf, i);
#endif
@ -149,9 +219,12 @@ WriteCSPRNG(void *threadid)
static int
ReadCSPRNG(void *threadid)
{
size_t tid;
uint8_t *buf;
size_t tid, zsize;
uint8_t *buf, *zbuf;
int i;
#ifdef DEBUG
int j;
#endif
tid = (size_t)threadid;
printf("Thread #%zd starts\n", tid);
@ -164,42 +237,50 @@ ReadCSPRNG(void *threadid)
#endif
{
#ifdef RANDOM_YARROW
random_yarrow_read(NULL, 0);
random_yarrow_read(NULL, 1);
random_yarrow_pre_read();
random_yarrow_post_read();
#endif
#ifdef RANDOM_FORTUNA
random_fortuna_read(NULL, 0);
random_fortuna_read(NULL, 1);
random_fortuna_pre_read();
random_fortuna_post_read();
#endif
usleep(100);
}
for (i = 0; i < 100000; i++) {
buf = malloc(i);
zbuf = malloc(2*i + 1024);
if (i % 1000 == 0)
printf("Thread read %zd - %d %p\n", tid, i, buf);
if (buf != NULL) {
printf("Thread read %zd - %d\n", tid, i);
if (buf != NULL && zbuf != NULL) {
#ifdef RANDOM_YARROW
random_yarrow_read(NULL, 0);
random_yarrow_pre_read();
random_yarrow_read(buf, i);
random_yarrow_read(NULL, 1);
random_yarrow_post_read();
#endif
#ifdef RANDOM_FORTUNA
random_fortuna_read(NULL, 0);
random_fortuna_pre_read();
random_fortuna_read(buf, i);
random_fortuna_read(NULL, 1);
random_fortuna_post_read();
#endif
#if 0
{
int j;
zsize = block_deflate(buf, zbuf, i);
if (zsize < i)
printf("ERROR!! Compressible RNG output!\n");
#ifdef DEBUG
printf("RNG output:\n");
for (j = 0; j < i; j++) {
printf(" %02X", buf[j]);
if (j % 32 == 31 || j == i - 1)
printf("\n");
}
printf("Compressed output:\n");
for (j = 0; j < zsize; j++) {
printf(" %02X", zbuf[j]);
if (j % 32 == 31 || j == zsize - 1)
printf("\n");
}
#endif
free(zbuf);
free(buf);
}
usleep(100);
@ -228,7 +309,7 @@ main(int argc, char *argv[])
for (t = 0; t < NUM_THREADS; t++) {
printf("In main: creating thread %ld\n", t);
rc = thrd_create(&threads[t], (t == 0 ? RunHarvester : (t == 1 ? WriteCSPRNG : ReadCSPRNG)), t);
rc = thrd_create(&threads[t], (t == 0 ? RunHarvester : (t == 1 ? WriteCSPRNG : ReadCSPRNG)), NULL);
if (rc != thrd_success) {
printf("ERROR; return code from thrd_create() is %d\n", rc);
exit(-1);

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2013 Mark R V Murray
* Copyright (c) 2013-2015 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -28,20 +28,39 @@
#ifndef UNIT_TEST_H_INCLUDED
#define UNIT_TEST_H_INCLUDED
#define UNIT_TEST_H_INCLUDED
#ifdef _KERNEL
#error "Random unit tests cannot be compiled into the kernel."
#endif
void random_adaptor_unblock(void);
#if defined(clang) && __has_builtin(__builtin_readcyclecounter)
#define rdtsc __builtin_readcyclecounter
#else /* !clang */
#if defined(__amd64__) || defined(__i386__)
static __inline uint64_t
rdtsc(void)
{
uint32_t low, high;
__asm __volatile("rdtsc" : "=a" (low), "=d" (high));
return (low | ((uint64_t)high << 32));
}
#else /* __amd64__ || __i386__ */
#error "No rdtsc() implementation available."
#endif /* __amd64__ || __i386__ */
#endif /* !clang */
static __inline uint64_t
get_cyclecount(void)
{
/* Shaddup! */
return (4ULL);
return (rdtsc());
}
// #define PAGE_SIZE 4096
#define HARVESTSIZE 16
#define HARVESTSIZE 2
enum random_entropy_source {
RANDOM_START = 0,
@ -51,7 +70,7 @@ enum random_entropy_source {
struct harvest_event {
uintmax_t he_somecounter; /* fast counter for clock jitter */
uint8_t he_entropy[HARVESTSIZE];/* some harvested entropy */
uint32_t he_entropy[HARVESTSIZE];/* some harvested entropy */
u_int he_size; /* harvested entropy byte count */
u_int he_bits; /* stats about the entropy */
u_int he_destination; /* destination pool of this entropy */

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2000-2013 Mark R V Murray
* Copyright (c) 2000-2015 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -29,12 +29,12 @@
__FBSDID("$FreeBSD$");
#ifdef _KERNEL
#include "opt_random.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/random.h>
#include <sys/sysctl.h>
@ -47,13 +47,10 @@ __FBSDID("$FreeBSD$");
#include <dev/random/hash.h>
#include <dev/random/randomdev.h>
#include <dev/random/random_adaptors.h>
#include <dev/random/random_harvestq.h>
#include <dev/random/uint128.h>
#include <dev/random/yarrow.h>
#else /* !_KERNEL */
#include <sys/param.h>
#include <sys/types.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
@ -66,467 +63,403 @@ __FBSDID("$FreeBSD$");
#include <crypto/sha2/sha2.h>
#include <dev/random/hash.h>
#include <dev/random/randomdev.h>
#include <dev/random/uint128.h>
#include <dev/random/yarrow.h>
#endif /* _KERNEL */
#if !defined(RANDOM_YARROW) && !defined(RANDOM_FORTUNA)
#define RANDOM_YARROW
#elif defined(RANDOM_YARROW) && defined(RANDOM_FORTUNA)
#error "Must define either RANDOM_YARROW or RANDOM_FORTUNA"
#endif
#define RANDOM_YARROW_TIMEBIN 16 /* max value for Pt/t */
#if defined(RANDOM_YARROW)
#define RANDOM_YARROW_FAST 0
#define RANDOM_YARROW_SLOW 1
#define RANDOM_YARROW_NPOOLS 2
#define TIMEBIN 16 /* max value for Pt/t */
/* This algorithm (and code) presumes that RANDOM_KEYSIZE is twice as large as RANDOM_BLOCKSIZE */
CTASSERT(RANDOM_BLOCKSIZE == sizeof(uint128_t));
CTASSERT(RANDOM_KEYSIZE == 2*RANDOM_BLOCKSIZE);
#define FAST 0
#define SLOW 1
/* This algorithm (and code) presumes that KEYSIZE is twice as large as BLOCKSIZE */
CTASSERT(BLOCKSIZE == sizeof(uint128_t));
CTASSERT(KEYSIZE == 2*BLOCKSIZE);
/* This is the beastie that needs protecting. It contains all of the
* state that we are excited about.
* Exactly one is instantiated.
/*
* This is the beastie that needs protecting. It contains all of the
* state that we are excited about. Exactly one is instantiated.
*/
static struct yarrow_state {
union {
uint8_t byte[BLOCKSIZE];
uint128_t whole;
} counter; /* C */
struct randomdev_key key; /* K */
u_int gengateinterval; /* Pg */
u_int bins; /* Pt/t */
u_int outputblocks; /* count output blocks for gates */
u_int slowoverthresh; /* slow pool overthreshhold reseed count */
struct pool {
struct source {
u_int bits; /* estimated bits of entropy */
} source[ENTROPYSOURCE];/* ... per source */
u_int thresh; /* pool reseed threshhold */
struct randomdev_hash hash; /* accumulated entropy */
} pool[2]; /* pool[0] is fast, pool[1] is slow */
int seeded;
struct start_cache {
uint8_t junk[KEYSIZE];
struct randomdev_hash hash;
} start_cache;
uint128_t ys_counter; /* C */
struct randomdev_key ys_key; /* K */
u_int ys_gengateinterval; /* Pg */
u_int ys_bins; /* Pt/t */
u_int ys_outputblocks; /* count output blocks for gates */
u_int ys_slowoverthresh; /* slow pool overthreshhold reseed count */
struct ys_pool {
u_int ysp_source_bits[ENTROPYSOURCE]; /* estimated bits of entropy per source */
u_int ysp_thresh; /* pool reseed threshhold */
struct randomdev_hash ysp_hash; /* accumulated entropy */
} ys_pool[RANDOM_YARROW_NPOOLS];/* pool[0] is fast, pool[1] is slow */
int ys_seeded;
/* Reseed lock */
mtx_t ys_mtx;
} yarrow_state;
/* The random_reseed_mtx mutex protects seeding and polling/blocking. */
static mtx_t random_reseed_mtx;
#ifdef _KERNEL
static struct sysctl_ctx_list random_clist;
RANDOM_CHECK_UINT(gengateinterval, 4, 64);
RANDOM_CHECK_UINT(bins, 2, 16);
RANDOM_CHECK_UINT(fastthresh, (BLOCKSIZE*8)/4, (BLOCKSIZE*8)); /* Bit counts */
RANDOM_CHECK_UINT(slowthresh, (BLOCKSIZE*8)/4, (BLOCKSIZE*8)); /* Bit counts */
RANDOM_CHECK_UINT(bins, RANDOM_YARROW_NPOOLS, 16);
RANDOM_CHECK_UINT(fastthresh, (RANDOM_BLOCKSIZE*8)/4, (RANDOM_BLOCKSIZE*8)); /* Bit counts */
RANDOM_CHECK_UINT(slowthresh, (RANDOM_BLOCKSIZE*8)/4, (RANDOM_BLOCKSIZE*8)); /* Bit counts */
RANDOM_CHECK_UINT(slowoverthresh, 1, 5);
#else /* !_KERNEL */
static u_int harvest_destination[ENTROPYSOURCE];
#endif /* _KERNEL */
static void generator_gate(void);
static void reseed(u_int);
static void random_yarrow_pre_read(void);
static void random_yarrow_read(uint8_t *, u_int);
static void random_yarrow_post_read(void);
static void random_yarrow_write(uint8_t *, u_int);
static void random_yarrow_reseed(void);
static int random_yarrow_seeded(void);
static void random_yarrow_reseed_internal(u_int);
static void random_yarrow_process_event(struct harvest_event *);
void
random_yarrow_init_alg(void)
#ifdef _KERNEL
/* Interface to Adaptors system */
struct random_algorithm random_alg_context = {
.ra_ident = "Yarrow",
.ra_pre_read = random_yarrow_pre_read,
.ra_read = random_yarrow_read,
.ra_post_read = random_yarrow_post_read,
.ra_write = random_yarrow_write,
.ra_reseed = random_yarrow_reseed,
.ra_seeded = random_yarrow_seeded,
.ra_event_processor = random_yarrow_process_event,
.ra_poolcount = RANDOM_YARROW_NPOOLS,
};
#endif
/* ARGSUSED */
static void
random_yarrow_init_alg(void *unused __unused)
{
int i, j;
#ifdef _KERNEL
struct sysctl_oid *random_yarrow_o;
#endif /* _KERNEL */
memset(yarrow_state.start_cache.junk, 0, KEYSIZE);
randomdev_hash_init(&yarrow_state.start_cache.hash);
/* Set up the lock for the reseed/gate state */
#ifdef _KERNEL
mtx_init(&random_reseed_mtx, "reseed mutex", NULL, MTX_DEF);
#else /* !_KERNEL */
mtx_init(&random_reseed_mtx, mtx_plain);
#endif /* _KERNEL */
#endif
RANDOM_RESEED_INIT_LOCK();
/* Start unseeded, therefore blocked. */
yarrow_state.seeded = 0;
yarrow_state.ys_seeded = 0;
#ifdef _KERNEL
/* Yarrow parameters. Do not adjust these unless you have
/*
* Yarrow parameters. Do not adjust these unless you have
* have a very good clue about what they do!
*/
random_yarrow_o = SYSCTL_ADD_NODE(&random_clist,
SYSCTL_STATIC_CHILDREN(_kern_random),
OID_AUTO, "yarrow", CTLFLAG_RW, 0,
"Yarrow Parameters");
SYSCTL_ADD_PROC(&random_clist,
SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO,
"gengateinterval", CTLTYPE_INT|CTLFLAG_RW,
&yarrow_state.gengateinterval, 10,
random_check_uint_gengateinterval, "I",
"gengateinterval", CTLTYPE_UINT | CTLFLAG_RWTUN,
&yarrow_state.ys_gengateinterval, 0,
random_check_uint_gengateinterval, "UI",
"Generation gate interval");
SYSCTL_ADD_PROC(&random_clist,
SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO,
"bins", CTLTYPE_INT|CTLFLAG_RW,
&yarrow_state.bins, 10,
random_check_uint_bins, "I",
"bins", CTLTYPE_UINT | CTLFLAG_RWTUN,
&yarrow_state.ys_bins, 0,
random_check_uint_bins, "UI",
"Execution time tuner");
SYSCTL_ADD_PROC(&random_clist,
SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO,
"fastthresh", CTLTYPE_INT|CTLFLAG_RW,
&yarrow_state.pool[0].thresh, (3*(BLOCKSIZE*8))/4,
random_check_uint_fastthresh, "I",
"fastthresh", CTLTYPE_UINT | CTLFLAG_RWTUN,
&yarrow_state.ys_pool[0].ysp_thresh, 0,
random_check_uint_fastthresh, "UI",
"Fast reseed threshold");
SYSCTL_ADD_PROC(&random_clist,
SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO,
"slowthresh", CTLTYPE_INT|CTLFLAG_RW,
&yarrow_state.pool[1].thresh, (BLOCKSIZE*8),
random_check_uint_slowthresh, "I",
"slowthresh", CTLTYPE_UINT | CTLFLAG_RWTUN,
&yarrow_state.ys_pool[1].ysp_thresh, 0,
random_check_uint_slowthresh, "UI",
"Slow reseed threshold");
SYSCTL_ADD_PROC(&random_clist,
SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO,
"slowoverthresh", CTLTYPE_INT|CTLFLAG_RW,
&yarrow_state.slowoverthresh, 2,
random_check_uint_slowoverthresh, "I",
"slowoverthresh", CTLTYPE_UINT | CTLFLAG_RWTUN,
&yarrow_state.ys_slowoverthresh, 0,
random_check_uint_slowoverthresh, "UI",
"Slow over-threshold reseed");
#endif /* _KERNEL */
yarrow_state.gengateinterval = 10;
yarrow_state.bins = 10;
yarrow_state.pool[FAST].thresh = (3*(BLOCKSIZE*8))/4;
yarrow_state.pool[SLOW].thresh = (BLOCKSIZE*8);
yarrow_state.slowoverthresh = 2;
yarrow_state.ys_gengateinterval = 10;
yarrow_state.ys_bins = 10;
yarrow_state.ys_pool[RANDOM_YARROW_FAST].ysp_thresh = (3*(RANDOM_BLOCKSIZE*8))/4;
yarrow_state.ys_pool[RANDOM_YARROW_SLOW].ysp_thresh = (RANDOM_BLOCKSIZE*8);
yarrow_state.ys_slowoverthresh = 2;
/* Ensure that the first time we read, we are gated. */
yarrow_state.outputblocks = yarrow_state.gengateinterval;
yarrow_state.ys_outputblocks = yarrow_state.ys_gengateinterval;
/* Initialise the fast and slow entropy pools */
for (i = FAST; i <= SLOW; i++) {
randomdev_hash_init(&yarrow_state.pool[i].hash);
for (i = RANDOM_YARROW_FAST; i <= RANDOM_YARROW_SLOW; i++) {
randomdev_hash_init(&yarrow_state.ys_pool[i].ysp_hash);
for (j = RANDOM_START; j < ENTROPYSOURCE; j++)
yarrow_state.pool[i].source[j].bits = 0U;
yarrow_state.ys_pool[i].ysp_source_bits[j] = 0;
}
/* Clear the counter */
uint128_clear(&yarrow_state.counter.whole);
yarrow_state.ys_counter = UINT128_ZERO;
}
#ifdef _KERNEL
SYSINIT(random_yarrow, SI_SUB_RANDOM, SI_ORDER_THIRD, random_yarrow_init_alg, NULL);
#endif
void
random_yarrow_deinit_alg(void)
/* ARGSUSED */
static void
random_yarrow_deinit_alg(void *unused __unused)
{
mtx_destroy(&random_reseed_mtx);
memset(&yarrow_state, 0, sizeof(yarrow_state));
RANDOM_RESEED_DEINIT_LOCK();
explicit_bzero(&yarrow_state, sizeof(yarrow_state));
#ifdef _KERNEL
sysctl_ctx_free(&random_clist);
#endif
}
#ifdef _KERNEL
SYSUNINIT(random_yarrow, SI_SUB_RANDOM, SI_ORDER_THIRD, random_yarrow_deinit_alg, NULL);
#endif
static __inline void
random_yarrow_post_insert(void)
/* Process a single stochastic event off the harvest queue */
static void
random_yarrow_process_event(struct harvest_event *event)
{
u_int pl, overthreshhold[2];
u_int pl, overthreshhold[RANDOM_YARROW_NPOOLS];
enum random_entropy_source src;
#ifdef _KERNEL
mtx_assert(&random_reseed_mtx, MA_OWNED);
#endif
RANDOM_RESEED_LOCK();
/*
* Accumulate the event into the appropriate pool
* where each event carries the destination information.
* We lock against pool state modification which can happen
* during accumulation/reseeding and reading/regating
*/
pl = event->he_destination % RANDOM_YARROW_NPOOLS;
randomdev_hash_iterate(&yarrow_state.ys_pool[pl].ysp_hash, event, sizeof(*event));
yarrow_state.ys_pool[pl].ysp_source_bits[event->he_source] += event->he_bits;
/* Count the over-threshold sources in each pool */
for (pl = 0; pl < 2; pl++) {
for (pl = RANDOM_YARROW_FAST; pl <= RANDOM_YARROW_SLOW; pl++) {
overthreshhold[pl] = 0;
for (src = RANDOM_START; src < ENTROPYSOURCE; src++) {
if (yarrow_state.pool[pl].source[src].bits > yarrow_state.pool[pl].thresh)
if (yarrow_state.ys_pool[pl].ysp_source_bits[src] > yarrow_state.ys_pool[pl].ysp_thresh)
overthreshhold[pl]++;
}
}
/* If enough slow sources are over threshhold, then slow reseed
/*
* If enough slow sources are over threshhold, then slow reseed
* else if any fast source over threshhold, then fast reseed.
*/
if (overthreshhold[SLOW] >= yarrow_state.slowoverthresh)
reseed(SLOW);
else if (overthreshhold[FAST] > 0 && yarrow_state.seeded)
reseed(FAST);
if (overthreshhold[RANDOM_YARROW_SLOW] >= yarrow_state.ys_slowoverthresh)
random_yarrow_reseed_internal(RANDOM_YARROW_SLOW);
else if (overthreshhold[RANDOM_YARROW_FAST] > 0 && yarrow_state.ys_seeded)
random_yarrow_reseed_internal(RANDOM_YARROW_FAST);
explicit_bzero(event, sizeof(*event));
RANDOM_RESEED_UNLOCK();
}
/* Process a single stochastic event off the harvest queue */
void
random_yarrow_process_event(struct harvest_event *event)
{
u_int pl;
mtx_lock(&random_reseed_mtx);
/* Accumulate the event into the appropriate pool
* where each event carries the destination information.
* We lock against pool state modification which can happen
* during accumulation/reseeding and reading/regating
*/
pl = event->he_destination % 2;
randomdev_hash_iterate(&yarrow_state.pool[pl].hash, event, sizeof(*event));
yarrow_state.pool[pl].source[event->he_source].bits += event->he_bits;
random_yarrow_post_insert();
mtx_unlock(&random_reseed_mtx);
}
/* Process a block of data suspected to be slightly stochastic */
/* Process a block of data suspected to be slightly stochastic. */
static void
random_yarrow_process_buffer(uint8_t *buf, u_int length)
random_yarrow_process_buffer(uint32_t *buf, u_int wordcount)
{
static struct harvest_event event;
u_int i, pl;
static u_int destination = 0;
int i;
/* Accumulate the data into the appropriate pools
* where each event carries the destination information.
* We lock against pool state modification which can happen
* during accumulation/reseeding and reading/regating
*/
memset(event.he_entropy + sizeof(uint32_t), 0, HARVESTSIZE - sizeof(uint32_t));
for (i = 0; i < length/sizeof(uint32_t); i++) {
event.he_somecounter = get_cyclecount();
event.he_bits = 0; /* Fake */
for (i = 0; i < wordcount; i += sizeof(event.he_entropy)/sizeof(event.he_entropy[0])) {
event.he_somecounter = (uint32_t)get_cyclecount();
event.he_size = sizeof(event.he_entropy);
event.he_bits = event.he_size/8;
event.he_source = RANDOM_CACHED;
event.he_destination = harvest_destination[RANDOM_CACHED]++;
event.he_size = sizeof(uint32_t);
*((uint32_t *)event.he_entropy) = *((uint32_t *)buf + i);
/* Do the actual entropy insertion */
pl = event.he_destination % 2;
randomdev_hash_iterate(&yarrow_state.pool[pl].hash, &event, sizeof(event));
#ifdef DONT_DO_THIS_HERE
/* Don't do this here - do it in bulk at the end */
yarrow_state.pool[pl].source[RANDOM_CACHED].bits += bits;
#endif
event.he_destination = destination++; /* Harmless cheating */
memcpy(event.he_entropy, buf + i, sizeof(event.he_entropy));
random_yarrow_process_event(&event);
}
for (pl = FAST; pl <= SLOW; pl++)
yarrow_state.pool[pl].source[RANDOM_CACHED].bits += (length >> 4);
random_yarrow_post_insert();
}
static void
reseed(u_int fastslow)
random_yarrow_reseed_internal(u_int fastslow)
{
/* Interrupt-context stack is a limited resource; make large
/*
* Interrupt-context stack is a limited resource; make large
* structures static.
*/
static uint8_t v[TIMEBIN][KEYSIZE]; /* v[i] */
static uint8_t hash[KEYSIZE]; /* h' */
static uint8_t temp[KEYSIZE];
static uint8_t v[RANDOM_YARROW_TIMEBIN][RANDOM_KEYSIZE]; /* v[i] */
static uint128_t temp;
static struct randomdev_hash context;
u_int i;
enum random_entropy_source j;
KASSERT(yarrow_state.pool[FAST].thresh > 0, ("random: Yarrow fast threshold = 0"));
KASSERT(yarrow_state.pool[SLOW].thresh > 0, ("random: Yarrow slow threshold = 0"));
KASSERT(yarrow_state.ys_pool[RANDOM_YARROW_FAST].ysp_thresh > 0, ("random: Yarrow fast threshold = 0"));
KASSERT(yarrow_state.ys_pool[RANDOM_YARROW_SLOW].ysp_thresh > 0, ("random: Yarrow slow threshold = 0"));
RANDOM_RESEED_ASSERT_LOCK_OWNED();
#ifdef RANDOM_DEBUG
#ifdef RANDOM_DEBUG_VERBOSE
printf("random: %s %s\n", __func__, (fastslow == FAST ? "FAST" : "SLOW"));
/* WARNING! This is dangerously tedious to do with mutexes held! */
printf("random: %s %s seeded = %d\n", __func__, (fastslow == RANDOM_YARROW_FAST ? "RANDOM_YARROW_FAST" : "RANDOM_YARROW_SLOW"), yarrow_state.ys_seeded);
printf("random: %s - fast - thresh %d,1 - ", __func__, yarrow_state.ys_pool[RANDOM_YARROW_FAST].ysp_thresh);
for (i = RANDOM_START; i < ENTROPYSOURCE; i++)
printf(" %d", yarrow_state.ys_pool[RANDOM_YARROW_FAST].ysp_source_bits[i]);
printf("\n");
printf("random: %s - slow - thresh %d,%d - ", __func__, yarrow_state.ys_pool[RANDOM_YARROW_SLOW].ysp_thresh, yarrow_state.ys_slowoverthresh);
for (i = RANDOM_START; i < ENTROPYSOURCE; i++)
printf(" %d", yarrow_state.ys_pool[RANDOM_YARROW_SLOW].ysp_source_bits[i]);
printf("\n");
#endif
if (!yarrow_state.seeded) {
printf("random: %s - fast - thresh %d,1 - ", __func__, yarrow_state.pool[FAST].thresh);
for (i = RANDOM_START; i < ENTROPYSOURCE; i++)
printf(" %d", yarrow_state.pool[FAST].source[i].bits);
printf("\n");
printf("random: %s - slow - thresh %d,%d - ", __func__, yarrow_state.pool[SLOW].thresh, yarrow_state.slowoverthresh);
for (i = RANDOM_START; i < ENTROPYSOURCE; i++)
printf(" %d", yarrow_state.pool[SLOW].source[i].bits);
printf("\n");
}
#endif
#ifdef _KERNEL
mtx_assert(&random_reseed_mtx, MA_OWNED);
#endif
/* 1. Hash the accumulated entropy into v[0] */
randomdev_hash_init(&context);
/* Feed the slow pool hash in if slow */
if (fastslow == SLOW) {
randomdev_hash_finish(&yarrow_state.pool[SLOW].hash, temp);
randomdev_hash_iterate(&context, temp, sizeof(temp));
if (fastslow == RANDOM_YARROW_SLOW) {
randomdev_hash_finish(&yarrow_state.ys_pool[RANDOM_YARROW_SLOW].ysp_hash, &temp);
randomdev_hash_iterate(&context, &temp, sizeof(temp));
}
randomdev_hash_finish(&yarrow_state.pool[FAST].hash, temp);
randomdev_hash_iterate(&context, temp, sizeof(temp));
randomdev_hash_finish(&yarrow_state.ys_pool[RANDOM_YARROW_FAST].ysp_hash, &temp);
randomdev_hash_iterate(&context, &temp, sizeof(temp));
randomdev_hash_finish(&context, v[0]);
/* 2. Compute hash values for all v. _Supposed_ to be computationally
/*-
* 2. Compute hash values for all v. _Supposed_ to be computationally
* intensive.
*/
if (yarrow_state.bins > TIMEBIN)
yarrow_state.bins = TIMEBIN;
for (i = 1; i < yarrow_state.bins; i++) {
if (yarrow_state.ys_bins > RANDOM_YARROW_TIMEBIN)
yarrow_state.ys_bins = RANDOM_YARROW_TIMEBIN;
for (i = 1; i < yarrow_state.ys_bins; i++) {
randomdev_hash_init(&context);
/* v[i] #= h(v[i - 1]) */
randomdev_hash_iterate(&context, v[i - 1], KEYSIZE);
randomdev_hash_iterate(&context, v[i - 1], RANDOM_KEYSIZE);
/* v[i] #= h(v[0]) */
randomdev_hash_iterate(&context, v[0], KEYSIZE);
randomdev_hash_iterate(&context, v[0], RANDOM_KEYSIZE);
/* v[i] #= h(i) */
randomdev_hash_iterate(&context, &i, sizeof(i));
/* Return the hashval */
randomdev_hash_finish(&context, v[i]);
}
/* 3. Compute a new key; h' is the identity function here;
/*-
* 3. Compute a new key; h' is the identity function here;
* it is not being ignored!
*/
randomdev_hash_init(&context);
randomdev_hash_iterate(&context, &yarrow_state.key, KEYSIZE);
for (i = 1; i < yarrow_state.bins; i++)
randomdev_hash_iterate(&context, v[i], KEYSIZE);
randomdev_hash_finish(&context, temp);
randomdev_encrypt_init(&yarrow_state.key, temp);
randomdev_hash_iterate(&context, &yarrow_state.ys_key, RANDOM_KEYSIZE);
for (i = 1; i < yarrow_state.ys_bins; i++)
randomdev_hash_iterate(&context, v[i], RANDOM_KEYSIZE);
randomdev_hash_finish(&context, &temp);
randomdev_encrypt_init(&yarrow_state.ys_key, &temp);
/* 4. Recompute the counter */
uint128_clear(&yarrow_state.counter.whole);
randomdev_encrypt(&yarrow_state.key, yarrow_state.counter.byte, temp, BLOCKSIZE);
memcpy(yarrow_state.counter.byte, temp, BLOCKSIZE);
yarrow_state.ys_counter = UINT128_ZERO;
randomdev_encrypt(&yarrow_state.ys_key, &yarrow_state.ys_counter, &temp, RANDOM_BLOCKSIZE);
yarrow_state.ys_counter = temp;
/* 5. Reset entropy estimate accumulators to zero */
for (i = 0; i <= fastslow; i++)
for (j = RANDOM_START; j < ENTROPYSOURCE; j++)
yarrow_state.pool[i].source[j].bits = 0;
yarrow_state.ys_pool[i].ysp_source_bits[j] = 0;
/* 6. Wipe memory of intermediate values */
memset(v, 0, sizeof(v));
memset(temp, 0, sizeof(temp));
memset(hash, 0, sizeof(hash));
memset(&context, 0, sizeof(context));
#ifdef RANDOM_RWFILE_WRITE_IS_OK /* Not defined so writes ain't gonna happen */
/* 7. Dump to seed file */
/* This pseudo-code is documentation. Please leave it alone. */
explicit_bzero(v, sizeof(v));
explicit_bzero(&temp, sizeof(temp));
explicit_bzero(&context, sizeof(context));
/* Not defined so writes ain't gonna happen. Kept for documenting. */
#ifdef RANDOM_RWFILE_WRITE_IS_OK
/*-
* 7. Dump to seed file.
* This pseudo-code is documentation. Please leave it alone.
*/
seed_file = "<some file>";
error = randomdev_write_file(seed_file, <generated entropy>, PAGE_SIZE);
if (error == 0)
printf("random: entropy seed file '%s' successfully written\n", seed_file);
#endif
/* Unblock the device if it was blocked due to being unseeded */
if (!yarrow_state.seeded) {
yarrow_state.seeded = 1;
random_adaptor_unblock();
if (!yarrow_state.ys_seeded) {
yarrow_state.ys_seeded = 1;
randomdev_unblock();
}
}
/* Internal function to return processed entropy from the PRNG */
static __inline void
random_yarrow_generator_gate(void)
{
u_int i;
uint8_t temp[RANDOM_KEYSIZE];
RANDOM_RESEED_ASSERT_LOCK_OWNED();
uint128_increment(&yarrow_state.ys_counter);
for (i = 0; i < RANDOM_KEYSIZE; i += RANDOM_BLOCKSIZE)
randomdev_encrypt(&yarrow_state.ys_key, &yarrow_state.ys_counter, temp + i, RANDOM_BLOCKSIZE);
randomdev_encrypt_init(&yarrow_state.ys_key, temp);
explicit_bzero(temp, sizeof(temp));
}
/*-
* Used to return processed entropy from the PRNG.
* There is a pre_read and a post_read required to be present
* (but they can be null functions) in order to allow specific
* actions at the begin or the end of a read. Yarrow does its
* reseeding in its own thread. The _pre_read() and _post_read()
* are not used here, and must be kept for completeness.
*/
void
random_yarrow_pre_read(void)
{
}
/*-
* Main read from Yarrow.
* The supplied buf MUST be a multiple (>=0) of RANDOM_BLOCKSIZE in size.
* Lots of code presumes this for efficiency, both here and in other
* routines. You are NOT allowed to break this!
*/
void
random_yarrow_read(uint8_t *buf, u_int bytecount)
{
uint8_t tbuf[BLOCKSIZE];
u_int blockcount, i;
/* Check for initial/final read requests */
if (buf == NULL)
return;
/* The reseed task must not be jumped on */
mtx_lock(&random_reseed_mtx);
blockcount = (bytecount + BLOCKSIZE - 1)/BLOCKSIZE;
RANDOM_RESEED_LOCK();
blockcount = (bytecount + RANDOM_BLOCKSIZE - 1)/RANDOM_BLOCKSIZE;
for (i = 0; i < blockcount; i++) {
if (yarrow_state.outputblocks++ >= yarrow_state.gengateinterval) {
generator_gate();
yarrow_state.outputblocks = 0;
}
uint128_increment(&yarrow_state.counter.whole);
if ((i + 1) * BLOCKSIZE > bytecount) {
/* TODO: FIX! remove memcpy()! */
randomdev_encrypt(&yarrow_state.key,
yarrow_state.counter.byte, tbuf, BLOCKSIZE);
memcpy(buf, tbuf, bytecount - i * BLOCKSIZE);
} else {
randomdev_encrypt(&yarrow_state.key,
yarrow_state.counter.byte, buf, BLOCKSIZE);
buf += BLOCKSIZE;
if (yarrow_state.ys_outputblocks++ >= yarrow_state.ys_gengateinterval) {
random_yarrow_generator_gate();
yarrow_state.ys_outputblocks = 0;
}
uint128_increment(&yarrow_state.ys_counter);
randomdev_encrypt(&yarrow_state.ys_key, &yarrow_state.ys_counter, buf, RANDOM_BLOCKSIZE);
buf += RANDOM_BLOCKSIZE;
}
mtx_unlock(&random_reseed_mtx);
RANDOM_RESEED_UNLOCK();
}
/* Internal function to hand external entropy to the PRNG */
void
random_yarrow_post_read(void)
{
/* CWOT */
}
/* Internal function to hand external entropy to the PRNG. */
void
random_yarrow_write(uint8_t *buf, u_int count)
{
uintmax_t timestamp;
struct randomdev_hash hash;
uint32_t entropy_data[RANDOM_KEYSIZE_WORDS], timestamp;
/* We must be locked for all this as plenty of state gets messed with */
mtx_lock(&random_reseed_mtx);
timestamp = get_cyclecount();
randomdev_hash_iterate(&yarrow_state.start_cache.hash, &timestamp, sizeof(timestamp));
randomdev_hash_iterate(&yarrow_state.start_cache.hash, buf, count);
timestamp = get_cyclecount();
randomdev_hash_iterate(&yarrow_state.start_cache.hash, &timestamp, sizeof(timestamp));
randomdev_hash_finish(&yarrow_state.start_cache.hash, yarrow_state.start_cache.junk);
randomdev_hash_init(&yarrow_state.start_cache.hash);
#ifdef RANDOM_DEBUG_VERBOSE
{
int i;
printf("random: %s - ", __func__);
for (i = 0; i < KEYSIZE; i++)
printf("%02X", yarrow_state.start_cache.junk[i]);
printf("\n");
}
#endif
random_yarrow_process_buffer(yarrow_state.start_cache.junk, KEYSIZE);
memset(yarrow_state.start_cache.junk, 0, KEYSIZE);
mtx_unlock(&random_reseed_mtx);
}
static void
generator_gate(void)
{
u_int i;
uint8_t temp[KEYSIZE];
for (i = 0; i < KEYSIZE; i += BLOCKSIZE) {
uint128_increment(&yarrow_state.counter.whole);
randomdev_encrypt(&yarrow_state.key, yarrow_state.counter.byte, temp + i, BLOCKSIZE);
}
randomdev_encrypt_init(&yarrow_state.key, temp);
memset(temp, 0, KEYSIZE);
/* Extra timing here is helpful to scrape scheduler timing entropy */
randomdev_hash_init(&hash);
timestamp = (uint32_t)get_cyclecount();
randomdev_hash_iterate(&hash, &timestamp, sizeof(timestamp));
randomdev_hash_iterate(&hash, buf, count);
timestamp = (uint32_t)get_cyclecount();
randomdev_hash_iterate(&hash, &timestamp, sizeof(timestamp));
randomdev_hash_finish(&hash, entropy_data);
explicit_bzero(&hash, sizeof(hash));
random_yarrow_process_buffer(entropy_data, sizeof(entropy_data)/sizeof(entropy_data[0]));
explicit_bzero(entropy_data, sizeof(entropy_data));
}
void
random_yarrow_reseed(void)
{
mtx_lock(&random_reseed_mtx);
reseed(SLOW);
mtx_unlock(&random_reseed_mtx);
RANDOM_RESEED_LOCK();
random_yarrow_reseed_internal(RANDOM_YARROW_SLOW);
RANDOM_RESEED_UNLOCK();
}
int
random_yarrow_seeded(void)
{
return (yarrow_state.seeded);
return (yarrow_state.ys_seeded);
}
#endif /* RANDOM_YARROW */

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2000-2013 Mark R V Murray
* Copyright (c) 2000-2015 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -27,18 +27,21 @@
*/
#ifndef SYS_DEV_RANDOM_YARROW_H_INCLUDED
#define SYS_DEV_RANDOM_YARROW_H_INCLUDED
#define SYS_DEV_RANDOM_YARROW_H_INCLUDED
#ifdef _KERNEL
typedef struct mtx mtx_t;
#define RANDOM_RESEED_INIT_LOCK(x) mtx_init(&yarrow_state.ys_mtx, "reseed mutex", NULL, MTX_DEF)
#define RANDOM_RESEED_DEINIT_LOCK(x) mtx_destroy(&yarrow_state.ys_mtx)
#define RANDOM_RESEED_LOCK(x) mtx_lock(&yarrow_state.ys_mtx)
#define RANDOM_RESEED_UNLOCK(x) mtx_unlock(&yarrow_state.ys_mtx)
#define RANDOM_RESEED_ASSERT_LOCK_OWNED(x) mtx_assert(&yarrow_state.ys_mtx, MA_OWNED)
#else
#define RANDOM_RESEED_INIT_LOCK(x) mtx_init(&yarrow_state.ys_mtx, mtx_plain)
#define RANDOM_RESEED_DEINIT_LOCK(x) mtx_destroy(&yarrow_state.ys_mtx)
#define RANDOM_RESEED_LOCK(x) mtx_lock(&yarrow_state.ys_mtx)
#define RANDOM_RESEED_UNLOCK(x) mtx_unlock(&yarrow_state.ys_mtx)
#define RANDOM_RESEED_ASSERT_LOCK_OWNED(x)
#endif
void random_yarrow_init_alg(void);
void random_yarrow_deinit_alg(void);
void random_yarrow_read(uint8_t *, u_int);
void random_yarrow_write(uint8_t *, u_int);
void random_yarrow_reseed(void);
int random_yarrow_seeded(void);
void random_yarrow_process_event(struct harvest_event *event);
#endif
#endif /* SYS_DEV_RANDOM_YARROW_H_INCLUDED */

View File

@ -145,16 +145,9 @@ rndtest_harvest(struct rndtest_state *rsp, void *buf, u_int len)
*/
if (rsp->rs_discard)
rndstats.rst_discard += len;
else {
#if __FreeBSD_version < 500000
/* XXX verify buffer is word aligned */
u_int32_t *p = buf;
for (len /= sizeof (u_int32_t); len; len--)
add_true_randomness(*p++);
#else
random_harvest(buf, len, len*NBBY/2, RANDOM_PURE_RNDTEST);
#endif
}
else
/* MarkM: FIX!! Check that this does not swamp the harvester! */
random_harvest_queue(buf, len, len*NBBY/2, RANDOM_PURE_RNDTEST);
}
static void

View File

@ -211,7 +211,8 @@ safe_partname(struct safe_softc *sc)
static void
default_harvest(struct rndtest_state *rsp, void *buf, u_int count)
{
random_harvest(buf, count, count*NBBY/2, RANDOM_PURE_SAFE);
/* MarkM: FIX!! Check that this does not swamp the harvester! */
random_harvest_queue(buf, count, count*NBBY/2, RANDOM_PURE_SAFE);
}
#endif /* SAFE_NO_RNG */

View File

@ -666,7 +666,7 @@ sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
mouse = (mouse_info_t*)data;
random_harvest(mouse, sizeof(mouse_info_t), 2, RANDOM_MOUSE);
random_harvest_queue(mouse, sizeof(mouse_info_t), 2, RANDOM_MOUSE);
if (cmd == OLD_CONS_MOUSECTL) {
static u_char swapb[] = { 0, 4, 2, 6, 1, 5, 3, 7 };

View File

@ -3411,7 +3411,7 @@ scgetc(sc_softc_t *sc, u_int flags)
sc_touch_scrn_saver();
if (!(flags & SCGETC_CN))
random_harvest(&c, sizeof(c), 1, RANDOM_KEYBOARD);
random_harvest_queue(&c, sizeof(c), 1, RANDOM_KEYBOARD);
if (scp->kbd_mode != K_XLATE)
return KEYCHAR(c);

View File

@ -259,7 +259,8 @@ ubsec_partname(struct ubsec_softc *sc)
static void
default_harvest(struct rndtest_state *rsp, void *buf, u_int count)
{
random_harvest(buf, count, count*NBBY/2, RANDOM_PURE_UBSEC);
/* MarkM: FIX!! Check that this does not swamp the harvester! */
random_harvest_queue(buf, count, count*NBBY/2, RANDOM_PURE_UBSEC);
}
static int

View File

@ -215,7 +215,7 @@ vtrnd_harvest(struct vtrnd_softc *sc)
virtqueue_notify(vq);
virtqueue_poll(vq, NULL);
random_harvest(&value, sizeof(value), sizeof(value) * NBBY / 2,
random_harvest_queue(&value, sizeof(value), sizeof(value) * NBBY / 2,
RANDOM_PURE_VIRTIO);
}

View File

@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$");
#include <sys/power.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/random.h>
#include <sys/reboot.h>
#include <sys/systm.h>
#include <sys/terminal.h>
@ -732,6 +733,7 @@ vt_processkey(keyboard_t *kbd, struct vt_device *vd, int c)
{
struct vt_window *vw = vd->vd_curwindow;
random_harvest_queue(&c, sizeof(c), 1, RANDOM_KEYBOARD);
#if VT_ALT_TO_ESC_HACK
if (c & RELKEY) {
switch (c & ~RELKEY) {

View File

@ -139,7 +139,7 @@ sysmouse_process_event(mouse_info_t *mi)
unsigned char buf[MOUSE_SYS_PACKETSIZE];
int x, y, iy, z;
random_harvest(mi, sizeof *mi, 2, RANDOM_MOUSE);
random_harvest_queue(mi, sizeof *mi, 2, RANDOM_MOUSE);
mtx_lock(&sysmouse_lock);
switch (mi->operation) {

View File

@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
#include <sys/namei.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/random.h>
#include <sys/rwlock.h>
#include <sys/stat.h>
#include <sys/systm.h>
@ -1758,6 +1759,8 @@ tmpfs_itimes(struct vnode *vp, const struct timespec *acc,
}
node->tn_status &=
~(TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED);
/* XXX: FIX? The entropy here is desirable, but the harvesting may be expensive */
random_harvest_queue(node, sizeof(*node), 1, RANDOM_FS_ATIME);
}
void

View File

@ -888,7 +888,7 @@ intr_event_schedule_thread(struct intr_event *ie)
if (ie->ie_flags & IE_ENTROPY) {
entropy.event = (uintptr_t)ie;
entropy.td = ctd;
random_harvest(&entropy, sizeof(entropy), 2, RANDOM_INTERRUPT);
random_harvest_queue(&entropy, sizeof(entropy), 2, RANDOM_INTERRUPT);
}
KASSERT(p != NULL, ("ithread %s has no process", ie->ie_name));
@ -1039,7 +1039,7 @@ intr_event_schedule_thread(struct intr_event *ie, struct intr_thread *it)
if (ie->ie_flags & IE_ENTROPY) {
entropy.event = (uintptr_t)ie;
entropy.td = ctd;
random_harvest(&entropy, sizeof(entropy), 2, RANDOM_INTERRUPT);
random_harvest_queue(&entropy, sizeof(entropy), 2, RANDOM_INTERRUPT);
}
KASSERT(p != NULL, ("ithread %s has no process", ie->ie_name));
@ -1126,7 +1126,7 @@ swi_sched(void *cookie, int flags)
entropy.event = (uintptr_t)ih;
entropy.td = curthread;
random_harvest(&entropy, sizeof(entropy), 1, RANDOM_SWI);
random_harvest_queue(&entropy, sizeof(entropy), 1, RANDOM_SWI);
/*
* Set ih_need for this handler so that if the ithread is already

View File

@ -43,16 +43,17 @@ __FBSDID("$FreeBSD$");
#include "opt_config.h"
#include <sys/param.h>
#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/sbuf.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/proc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/jail.h>
#include <sys/proc.h>
#include <sys/random.h>
#include <sys/sbuf.h>
#include <sys/smp.h>
#include <sys/sx.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/unistd.h>
SYSCTL_ROOT_NODE(0, sysctl, CTLFLAG_RW, 0,
@ -152,10 +153,15 @@ sysctl_kern_arnd(SYSCTL_HANDLER_ARGS)
char buf[256];
size_t len;
len = req->oldlen;
if (len > sizeof(buf))
len = sizeof(buf);
arc4rand(buf, len, 0);
/*-
* This is one of the very few legitimate uses of read_random(9).
* 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));
}

View File

@ -28,7 +28,6 @@
__FBSDID("$FreeBSD$");
#include "opt_bus.h"
#include "opt_random.h"
#include <sys/param.h>
#include <sys/conf.h>
@ -2877,14 +2876,16 @@ device_attach(device_t dev)
attachtime = get_cyclecount() - attachtime;
/*
* 4 bits per device is a reasonable value for desktop and server
* hardware with good get_cyclecount() implementations, but may
* hardware with good get_cyclecount() implementations, but WILL
* need to be adjusted on other platforms.
*/
#ifdef RANDOM_DEBUG
printf("random: %s(): feeding %d bit(s) of entropy from %s%d\n",
__func__, 4, dev->driver->name, dev->unit);
#endif
random_harvest(&attachtime, sizeof(attachtime), 4, RANDOM_ATTACH);
#define RANDOM_PROBE_BIT_GUESS 4
if (bootverbose)
printf("random: harvesting attach, %zu bytes (%d bits) from %s%d\n",
sizeof(attachtime), RANDOM_PROBE_BIT_GUESS,
dev->driver->name, dev->unit);
random_harvest_direct(&attachtime, sizeof(attachtime),
RANDOM_PROBE_BIT_GUESS, RANDOM_ATTACH);
device_sysctl_update(dev);
if (dev->busy)
dev->state = DS_BUSY;

View File

@ -22,7 +22,7 @@ __FBSDID("$FreeBSD$");
#define ARC4_RESEED_BYTES 65536
#define ARC4_RESEED_SECONDS 300
#define ARC4_KEYBYTES (256 / 8)
#define ARC4_KEYBYTES 256
int arc4rand_iniseed_state = ARC4_ENTR_NONE;
@ -48,39 +48,33 @@ arc4_swap(u_int8_t *a, u_int8_t *b)
* Stir our S-box.
*/
static void
arc4_randomstir (void)
arc4_randomstir(void)
{
u_int8_t key[256];
int r, n;
u_int8_t key[ARC4_KEYBYTES];
int n;
struct timeval tv_now;
/*
* XXX read_random() returns unsafe numbers if the entropy
* device is not loaded -- MarkM.
* XXX: FIX!! This isn't brilliant. Need more confidence.
* This returns zero entropy before random(4) is seeded.
*/
r = read_random(key, ARC4_KEYBYTES);
(void)read_random(key, ARC4_KEYBYTES);
getmicrouptime(&tv_now);
mtx_lock(&arc4_mtx);
/* If r == 0 || -1, just use what was on the stack. */
if (r > 0) {
for (n = r; n < sizeof(key); n++)
key[n] = key[n % r];
}
for (n = 0; n < 256; n++) {
arc4_j = (arc4_j + arc4_sbox[n] + key[n]) % 256;
arc4_swap(&arc4_sbox[n], &arc4_sbox[arc4_j]);
}
arc4_i = arc4_j = 0;
/* Reset for next reseed cycle. */
arc4_t_reseed = tv_now.tv_sec + ARC4_RESEED_SECONDS;
arc4_numruns = 0;
/*
* Throw away the first N words of output, as suggested in the
* paper "Weaknesses in the Key Scheduling Algorithm of RC4"
* by Fluher, Mantin, and Shamir. (N = 256 in our case.)
*
* http://dl.acm.org/citation.cfm?id=646557.694759
*/
for (n = 0; n < 256*4; n++)
arc4_randbyte();

View File

@ -50,7 +50,7 @@ srandom(seed)
}
/*
* Pseudo-random number generator for randomizing the profiling clock,
* Pseudo-random number generator for perturbing the profiling clock,
* and whatever else we might use it for. The result is uniform on
* [0, 2^31 - 1].
*/

View File

@ -125,7 +125,8 @@ octeon_rnd_harvest(void *arg)
for (i = 0; i < OCTEON_RND_WORDS; i++)
sc->sc_entropy[i] = cvmx_rng_get_random64();
random_harvest(sc->sc_entropy, sizeof sc->sc_entropy,
/* MarkM: FIX!! Check that this does not swamp the harvester! */
random_harvest_queue(sc->sc_entropy, sizeof sc->sc_entropy,
(sizeof(sc->sc_entropy)*8)/2, RANDOM_PURE_OCTEON);
callout_reset(&sc->sc_callout, hz * 5, octeon_rnd_harvest, sc);

View File

@ -24,7 +24,7 @@ makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols
# Build these as modules so small platform builds will have the
# modules already built.
makeoptions MODULES_OVERRIDE="random gpio ar71xx if_gif if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_pci"
makeoptions MODULES_OVERRIDE="gpio ar71xx if_gif if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_pci"
# For small memory footprints
options VM_KMEM_SIZE_SCALE=1

View File

@ -25,7 +25,7 @@ makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols
# Build these as modules so small platform builds will have the
# modules already built.
makeoptions MODULES_OVERRIDE="random gpio ar71xx if_gif if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_pci hwpmc cam"
makeoptions MODULES_OVERRIDE="gpio ar71xx if_gif if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_pci hwpmc cam"
# For small memory footprints
options VM_KMEM_SIZE_SCALE=1

View File

@ -20,7 +20,7 @@ files "../atheros/files.ar71xx"
hints "AR91XX_BASE.hints"
makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols
makeoptions MODULES_OVERRIDE="random gpio ar71xx if_gif if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_ahb hwpmc"
makeoptions MODULES_OVERRIDE="gpio ar71xx if_gif if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_ahb hwpmc"
options DDB
options KDB

View File

@ -20,7 +20,7 @@ files "../atheros/files.ar71xx"
hints "AR933X_BASE.hints"
makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols
makeoptions MODULES_OVERRIDE="random gpio ar71xx if_gif if_vlan if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr hwpmc ipfw"
makeoptions MODULES_OVERRIDE="gpio ar71xx if_gif if_vlan if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr hwpmc ipfw"
options DDB
options KDB

View File

@ -20,7 +20,7 @@ files "../atheros/files.ar71xx"
hints "AR934X_BASE.hints"
makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols
makeoptions MODULES_OVERRIDE="random gpio ar71xx if_gif if_gre if_vlan if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_ahb hwpmc ipfw ipfw_nat libalias"
makeoptions MODULES_OVERRIDE="gpio ar71xx if_gif if_gre if_vlan if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_ahb hwpmc ipfw ipfw_nat libalias"
# makeoptions MODULES_OVERRIDE=""
options DDB

View File

@ -22,7 +22,7 @@ options AR71XX_ENV_UBOOT
# who already are using it without modifying the default flash layout)
# we need to cut down on a lot of things.
makeoptions MODULES_OVERRIDE="ath ath_pci ath_ahb bridgestp if_bridge if_gif if_gre random wlan wlan_acl wlan_amrr wlan_ccmp wlan_rssadapt wlan_tkip wlan_wep wlan_xauth usb ar71xx"
makeoptions MODULES_OVERRIDE="ath ath_pci ath_ahb bridgestp if_bridge if_gif if_gre wlan wlan_acl wlan_amrr wlan_ccmp wlan_rssadapt wlan_tkip wlan_wep wlan_xauth usb ar71xx"
hints "PB92.hints"
include "../atheros/std.ar71xx"

View File

@ -26,7 +26,7 @@ files "../atheros/files.ar71xx"
hints "QCA955X_BASE.hints"
makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols
# makeoptions MODULES_OVERRIDE="random gpio ar71xx if_gif if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_ahb hwpmc"
# makeoptions MODULES_OVERRIDE="gpio ar71xx if_gif if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_ahb hwpmc"
makeoptions MODULES_OVERRIDE="if_vlan ipfw if_gre if_gif if_bridge bridgestp"
options DDB

View File

@ -24,7 +24,7 @@ makeoptions MIPS_LITTLE_ENDIAN=defined
makeoptions KERNLOADADDR=0x80001000
# Don't build any modules yet.
makeoptions MODULES_OVERRIDE="wlan_xauth wlan_wep wlan_tkip wlan_acl wlan_amrr wlan_ccmp wlan_rssadapt random if_bridge bridgestp msdosfs md ipfw dummynet libalias geom/geom_label ufs usb/uplcom usb/u3g usb/umodem usb/umass usb/ucom cam zlib"
makeoptions MODULES_OVERRIDE="wlan_xauth wlan_wep wlan_tkip wlan_acl wlan_amrr wlan_ccmp wlan_rssadapt if_bridge bridgestp msdosfs md ipfw dummynet libalias geom/geom_label ufs usb/uplcom usb/u3g usb/umodem usb/umass usb/ucom cam zlib"
makeoptions RT3052F
include "../rt305x/std.rt305x"

View File

@ -291,7 +291,6 @@ SUBDIR= \
${_qlxgbe} \
ral \
${_ralfw} \
${_random} \
rc4 \
${_rdma} \
${_rdrand_rng} \
@ -395,9 +394,6 @@ _autofs= autofs
_crypto= crypto
_cryptodev= cryptodev
.endif
.if exists(${.CURDIR}/../crypto)
_random= random
.endif
.endif
.if ${MK_CUSE} != "no" || defined(ALL_MODULES)

View File

@ -12,7 +12,7 @@
KMOD = crypto
SRCS = crypto.c cryptodev_if.c
SRCS += criov.c cryptosoft.c xform.c
SRCS += cast.c cryptodeflate.c rmd160.c rijndael-alg-fst.c rijndael-api.c
SRCS += cast.c cryptodeflate.c rmd160.c rijndael-alg-fst.c rijndael-api.c rijndael-api-fst.c
SRCS += skipjack.c bf_enc.c bf_ecb.c bf_skey.c
SRCS += camellia.c camellia-api.c
SRCS += des_ecb.c des_enc.c des_setkey.c

View File

@ -1,15 +0,0 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../dev/random
.PATH: ${.CURDIR}/../../crypto/rijndael
.PATH: ${.CURDIR}/../../crypto/sha2
KMOD= random
SRCS= randomdev_soft.c
SRCS+= yarrow.c fortuna.c hash.c
SRCS+= rijndael-alg-fst.c rijndael-api-fst.c sha2.c sha256c.c
SRCS+= bus_if.h device_if.h vnode_if.h opt_cpu.h opt_random.h
CFLAGS+= -I${.CURDIR}/../..
.include <bsd.kmod.mk>

View File

@ -426,6 +426,8 @@ ether_input_internal(struct ifnet *ifp, struct mbuf *m)
}
#endif
random_harvest_queue(m, sizeof(*m), 2, RANDOM_NET_ETHER);
CURVNET_SET_QUIET(ifp->if_vnet);
if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
@ -570,8 +572,6 @@ ether_input_internal(struct ifnet *ifp, struct mbuf *m)
m->m_flags |= M_PROMISC;
}
random_harvest(&(m->m_data), 12, 2, RANDOM_NET_ETHER);
ether_demux(ifp, m);
CURVNET_RESTORE();
}

View File

@ -906,7 +906,7 @@ tunwrite(struct cdev *dev, struct uio *uio, int flag)
m_freem(m);
return (EAFNOSUPPORT);
}
random_harvest(&(m->m_data), 12, 2, RANDOM_NET_TUN);
random_harvest_queue(&(m->m_data), 12, 2, RANDOM_NET_TUN);
if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);
if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
CURVNET_SET(ifp->if_vnet);

View File

@ -705,7 +705,7 @@ ng_iface_rcvdata(hook_p hook, item_p item)
m_freem(m);
return (EAFNOSUPPORT);
}
random_harvest(&(m->m_data), 12, 2, RANDOM_NET_NG);
random_harvest_queue(&(m->m_data), 12, 2, RANDOM_NET_NG);
M_SETFIB(m, ifp->if_fib);
netisr_dispatch(isr, m);
return (0);

View File

@ -107,8 +107,8 @@ enum sysinit_sub_id {
SI_SUB_KLD = 0x2000000, /* KLD and module setup */
SI_SUB_CPU = 0x2100000, /* CPU resource(s)*/
SI_SUB_RACCT = 0x2110000, /* resource accounting */
SI_SUB_RANDOM = 0x2120000, /* random number generator */
SI_SUB_KDTRACE = 0x2140000, /* Kernel dtrace hooks */
SI_SUB_RANDOM = 0x2160000, /* random number generator */
SI_SUB_MAC = 0x2180000, /* TrustedBSD MAC subsystem */
SI_SUB_MAC_POLICY = 0x21C0000, /* TrustedBSD MAC policies */
SI_SUB_MAC_LATE = 0x21D0000, /* TrustedBSD MAC subsystem */

View File

@ -31,7 +31,7 @@
#ifdef _KERNEL
int read_random(void *, int);
u_int read_random(void *, u_int);
/*
* Note: if you add or remove members of random_entropy_source, remember to also update the
@ -53,9 +53,10 @@ enum random_entropy_source {
RANDOM_NET_NG,
RANDOM_INTERRUPT,
RANDOM_SWI,
RANDOM_UMA_ALLOC,
RANDOM_ENVIRONMENTAL_END, /* This one is wasted */
/* High-quality HW RNGs from here on. */
RANDOM_FS_ATIME,
RANDOM_FAST, /* Special!! Miscellaneous high performance stuff, like UMA/SLAB Allocator */
RANDOM_ENVIRONMENTAL_END = RANDOM_FAST,
/* Fast hardware random-number sources from here on. */
RANDOM_PURE_OCTEON,
RANDOM_PURE_SAFE,
RANDOM_PURE_GLXSB,
@ -67,7 +68,18 @@ enum random_entropy_source {
RANDOM_PURE_VIRTIO,
ENTROPYSOURCE
};
void random_harvest(const void *, u_int, u_int, enum random_entropy_source);
#define RANDOM_HARVEST_EVERYTHING_MASK ((1 << (RANDOM_ENVIRONMENTAL_END + 1)) - 1)
#if defined(RANDOM_DUMMY)
#define random_harvest_queue(a, b, c, d) do {} while (0)
#define random_harvest_fast(a, b, c, d) do {} while (0)
#define random_harvest_direct(a, b, c, d) do {} while (0)
#else /* !defined(RANDOM_DUMMY) */
void random_harvest_queue(const void *, u_int, u_int, enum random_entropy_source);
void random_harvest_fast(const void *, u_int, u_int, enum random_entropy_source);
void random_harvest_direct(const void *, u_int, u_int, enum random_entropy_source);
#endif /* defined(RANDOM_DUMMY) */
#endif /* _KERNEL */

View File

@ -36,16 +36,17 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/bio.h>
#include <sys/buf.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/random.h>
#include <sys/resourcevar.h>
#include <sys/rwlock.h>
#include <sys/vmmeter.h>
#include <sys/stat.h>
#include <sys/vmmeter.h>
#include <sys/vnode.h>
#include <vm/vm.h>
#include <vm/vm_extern.h>
@ -143,12 +144,17 @@ ffs_update(vp, waitfor)
softdep_update_inodeblock(ip, bp, waitfor);
else if (ip->i_effnlink != ip->i_nlink)
panic("ffs_update: bad link cnt");
if (ip->i_ump->um_fstype == UFS1)
if (ip->i_ump->um_fstype == UFS1) {
*((struct ufs1_dinode *)bp->b_data +
ino_to_fsbo(fs, ip->i_number)) = *ip->i_din1;
else
/* XXX: FIX? The entropy here is desirable, but the harvesting may be expensive */
random_harvest_queue(&(ip->i_din1), sizeof(ip->i_din1), 1, RANDOM_FS_ATIME);
} else {
*((struct ufs2_dinode *)bp->b_data +
ino_to_fsbo(fs, ip->i_number)) = *ip->i_din2;
/* XXX: FIX? The entropy here is desirable, but the harvesting may be expensive */
random_harvest_queue(&(ip->i_din2), sizeof(ip->i_din2), 1, RANDOM_FS_ATIME);
}
if (waitfor && !DOINGASYNC(vp))
error = bwrite(bp);
else if (vm_page_count_severe() || buf_dirty_count_severe()) {

View File

@ -2135,11 +2135,8 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags)
int lockfail;
int cpu;
#if 0
/* XXX: FIX!! Do not enable this in CURRENT!! MarkM */
/* The entropy here is desirable, but the harvesting is expensive */
random_harvest(&(zone->uz_name), sizeof(void *), 1, RANDOM_UMA_ALLOC);
#endif
/* XXX: FIX? The entropy here is desirable, but the harvesting may be expensive */
random_harvest_fast(&zone, sizeof(zone), 1, RANDOM_FAST);
/* This is the fast path allocation */
#ifdef UMA_DEBUG_ALLOC_1
@ -2171,11 +2168,6 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags)
zone->uz_fini(item, zone->uz_size);
return (NULL);
}
#if 0
/* XXX: FIX!! Do not enable this in CURRENT!! MarkM */
/* The entropy here is desirable, but the harvesting is expensive */
random_harvest(&item, sizeof(void *), 1, RANDOM_UMA_ALLOC);
#endif
return (item);
}
/* This is unfortunate but should not be fatal. */
@ -2218,11 +2210,6 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags)
#endif
if (flags & M_ZERO)
uma_zero_item(item, zone);
#if 0
/* XXX: FIX!! Do not enable this in CURRENT!! MarkM */
/* The entropy here is desirable, but the harvesting is expensive */
random_harvest(&item, sizeof(void *), 1, RANDOM_UMA_ALLOC);
#endif
return (item);
}
@ -2343,11 +2330,6 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags)
zalloc_item:
item = zone_alloc_item(zone, udata, flags);
#if 0
/* XXX: FIX!! Do not enable this in CURRENT!! MarkM */
/* The entropy here is desirable, but the harvesting is expensive */
random_harvest(&item, sizeof(void *), 1, RANDOM_UMA_ALLOC);
#endif
return (item);
}
@ -2695,18 +2677,8 @@ uma_zfree_arg(uma_zone_t zone, void *item, void *udata)
int lockfail;
int cpu;
#if 0
/* XXX: FIX!! Do not enable this in CURRENT!! MarkM */
/* The entropy here is desirable, but the harvesting is expensive */
struct entropy {
const void *uz_name;
const void *item;
} entropy;
entropy.uz_name = zone->uz_name;
entropy.item = item;
random_harvest(&entropy, sizeof(struct entropy), 2, RANDOM_UMA_ALLOC);
#endif
/* XXX: FIX? The entropy here is desirable, but the harvesting may be expensive */
random_harvest_fast(&zone, sizeof(zone), 1, RANDOM_FAST);
#ifdef UMA_DEBUG_ALLOC_1
printf("Freeing item %p to %s(%p)\n", item, zone->uz_name, zone);