Edit for clarity: Does the "standard" test for initialization of the Linux non-blocking entropy pool, by attempting to read 1 byte from /dev/random, ignore seeding of /dev/urandom from file at boot, resulting in unnecessary hanging of any code that employs the "standard" test?
This article describes the Linux kernel as implementing three entropy pools; an input pool which feeds the CSPRNG and two output pools that are fed by the CSPRNG. One output pool is a blocking pool accessed by /dev/random and the other is a non-blocking pool accessed by /dev/urandom.
This answer describes how the kernel prioritises the seeding of the non-blocking pool at boot time:
the first 128 bits of entropy which the system collects get mixed directly into the nonblocking pool, bypassing the input pool [...] And per http://lxr.free-electrons.com/source/drivers/char/random.c?v=4.5#L677, nonblocking_pool.initialized is set when its entropy_total exceeds 128 bits. So by the time anything is available from /dev/random, /dev/urandom has been seeded.
Virtual machines without access to entropy from the host kernel, headless devices, and low end hardware, may not provide sufficient entropy at boot to seed the non-blocking pool, leaving services which access /dev/urandom such as SSH and OpenSSL vulnerable. 'Mining Your Ps and Qs' suggest this "boot-time entropy hole" may be the cause of duplicate SSH and TLS keys.
To check that the non-blocking pool is seeded, a method is proposed in the question, answered categorically 'yes' in the answer above:
if I succeed at reading a single byte from /dev/random, does this imply that /dev/urandom is seeded?
Elsewhere, this method is claimed to be the norm:
the standard way to wait until the system entropy pool is seeded is to read a single byte from /dev/random
It seems that this is the approach adopted by libsodium according to this github comment and the source code, causing PHP to hang on initialization for 3-5 minutes after boot on the type of systems described above. My own experiments replicate the 3-5 minute hanging on a VPS with low-end hardware, unless additional entropy is provided by rng-tools or PHP is compiled without libsodium.
All modern Linux systems save a random seed, usually 512 bytes, at shutdown and seed the non-blocking pool with this file early in the boot process. The standard method is described in the man page for random as well as the kernel source code for random.c, and implemented in the VPS I used.
The fact that libsodium causes PHP to hang despite the non-blocking pool being seeded during boot leads to the following questions:
- Is it the case that seeding the non-blocking pool as above, essentially
cat /etc/random-seed > /dev/urandom, does not unblock the blocking pool?
- If so, is there no better approach for libsodium to test for seeding of the non-blocking pool? (auxiliary: what method does getrandom() use to test that "the urandom source has been initialized"?)
- Which pool is seeded by the above approach? My guess would be that the seed is going straight into the non-blocking pool, bypassing the input pool. The random man page and source code for random.c refer to "the entropy pool", but this is imprecise.
- If the blocking pool is unblocked by the above approach, then why is libsodium hanging? The libsodium usage manual suggests an entropy count less than 160 could cause initialization to hang, but I don't see where in the source code this would be the case.