tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: getrandom and getentropy



> Date: Sun, 3 May 2020 10:28:08 +0200
> From: Kurt Roeckx <kurt%roeckx.be@localhost>
> 
> [OpenBSD] seem to use RDRAND when it's available in the bootloader, or
> something else when it's not. It's still my understanding that
> the bootloader is responisble for providing the entropy. You can
> argue that it might not contain as much entropy as you would like
> in all cases.
> [...]
> Various of their drivers have support for RNGs that are available
> on hardware, which seems to include: amdpm, glxsb, pchb, hifn,
> safe, ccp, tpm, amlrng, bcmirng, bcmrng, mvrng, octrng, omrng,
> rkrng, urng, uonerng. It's unclear to me if any of them are used
> in the bootloader.

NetBSD has drivers for various hardware RNGs too which will generally
gather entropy before userland starts (usually as soon as they are
discovered during bus enumeration at boot), and on x86 NetBSD will
gather entropy from RDRAND/RDSEED very early on in the kernel boot:

https://nxr.netbsd.org/xref/src/sys/arch/amd64/amd64/machdep.c#1680

The NetBSD bootloader doesn't do anything with RDRAND/RDSEED, although
I'm not sure it makes much of a difference to do it in the bootloader
vs doing it this early on in the kernel proper.  That's why I say that
if what OpenBSD does satisfies you, what NetBSD does should probably
satisfy you too.

In any case, not every machine _has_ a hardware RNG, and not every
machine is necessarily seeded, which is why NetBSD still adopts a
blocking model available through /dev/random and perhaps soon through
getrandom.

> Date: Sun, 3 May 2020 10:48:41 +0200
> From: Kurt Roeckx <kurt%roeckx.be@localhost>
> 
> On Fri, May 01, 2020 at 07:19:09PM +0000, Taylor R Campbell wrote:
> > +Despite the name, this is secure as long as you only do it
> > +.Em after
> > +at least one successful call without
> > +.Dv GRND_INSECURE ,
> > +such as
> > +.Li "getrandom(..., 0)"
> 
> At which point calling with GRND_INSECURE is the same as calling
> with 0 ...

Generally yes, although the sysctl knob kern.entropy.depletion=1 may
cause it to block again, so that you can easily test the impact of
blocking on any application before you deploy it into the field where
conditions may be different as a kind of fault injection, whereas
getrandom(...,GRND_INSECURE) is guaranteed never to block as a
reliable part of the API contract everywhere.  Conceivably if a
process in a VM were migrated from one host to another,
getrandom(...,0) might also block twice in the same process.

Point is: getrandom(...,0) has blocking as part of the API contract in
edge cases, and getrandom(...,GRND_INSECURE) does not.

But in normal operation without kern.entropy.depletion=1, yes, you are
right.

(If you want to discuss whether the Linux API should have
GRND_INSECURE at all, that's more of a discussion for the LKML.  The
path has existed in most OSes for a couple decades, anyway -- whether
via /dev/urandom, or via getentropy, or via kern.arandom.)

> > +or
> > +.Li "getrandom(..., GRND_RANDOM)" ,
> > +or after reading at least one byte from
> > +.Pa /dev/random .
> 
> Note that this is not the cases anymore on Linux. After reading 1
> byte from /dev/random, /dev/urandom can still be unintialized, and
> so GRND_INSECURE is still insecure on Linux.

In Linux 5.6, both getrandom(GRND_RANDOM) and /dev/random call
wait_for_random_bytes(), which waits until crng_ready() is true,
before returning a single byte:

/dev/random: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/char/random.c?h=v5.6#n1836
getrandom(GRND_RANDOM): https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/char/random.c?h=v5.6#n2000
wait_for_random_bytes: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/char/random.c?h=v5.6#n1611

crng_ready() returns true only if crng_init > 1, which in turn is set
only if (a) the CPU provided data via RDRAND/RDSEED or equivalent and
all the PRNG state has been initialized, or (b) enough entropy has
been gathered that that the system (via credit_entropy_bits) or the
operator (via ioctl(RNDRESEEDCRNG)) decided to reseed:

crng_ready(): https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/char/random.c?h=v5.6#n464
crng_init = 2, option (a): https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/char/random.c?h=v5.6#n804
crng_init = 2, option (b): https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/char/random.c?h=v5.6#n949

The bytes returned from /dev/random and getrandom(GRND_RANDOM) are
then returned from the same source as /dev/urandom:

getrandom(GRND_RANDOM): https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/char/random.c?h=v5.6#n2004
/dev/random: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/char/random.c?h=v5.6#n1839
/dev/urandom: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/char/random.c?h=v5.6#n1828

This has not changed since 5.6 in Linus's master branch; the only
differences are around arch_get_random_* to initialize the pool with
RDRAND/RDSEED or other architecture-specific equivalent, and to tweak
the `entropy estimator'.

So I'm pretty sure getting a byte out of /dev/random or GRND_RANDOM
still implies that /dev/urandom or GRND_INSECURE is ready.  I may have
misread this, so I could be wrong -- but it would be a pretty weird
bug for Linux to have introduced in unifying the code paths.

> > +.It Dv GRND_RANDOM
> > +Block until the system entropy pool has full entropy; then generate a
> > +small amount of data.
> > +Equivalent to reading from
> > +.Pa /dev/random ;
> 
> You might want to read https://lwn.net/Articles/808575/
> 
> The article states among other things that GRND_RANDOM is a noop,
> it behaves just like passing 0.

It is a noop in Linux 5.6, but it was not a noop in, say, Linux 4.14,
which is still widely deployed.  So, in general, you can't expect to
be able to read more than a few bytes at a time out of GRND_RANDOM.

I don't think it's worth spending a lot of time on GRND_RANDOM; the
whole concept of GRND_RANDOM was silly to begin with and I'm only
adding it to maintain source compatibility with Linux.  Nobody should
use it, and that's why I added a note to the man page that it is
silly, and declined to add any usage examples.

Thanks for the review!


Home | Main Index | Thread Index | Old Index