openssl: RAND_DRBG uses uninitialized urandom read on linux
This message can be seen when openssl s_server is started early after reboot:
./openssl s_server
random: openssl: uninitialized urandom read (32 bytes read)
Using default temp DH parameters
ACCEPT
I have seen this with linux v4.18, and v4.14-stable. Previous linux versions are also affected, but do not report the error.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 55 (55 by maintainers)
Commits related to this issue
- Fix seeding from random device w/o getrandom syscall Use select to wait for /dev/random in readable state, but do not actually read anything from /dev/random, use /dev/urandom first. Use linux defin... — committed to bernd-edlinger/openssl by bernd-edlinger 5 years ago
- Fix seeding from random device w/o getrandom syscall Use select to wait for /dev/random in readable state, but do not actually read anything from /dev/random, use /dev/urandom first. Use linux defin... — committed to openssl/openssl by bernd-edlinger 5 years ago
Guys, some of you seem a bit confused about what
getentropy()andgetrandom()are and how they relate.getrandom()is a syscallgetentropy()is a library routine that usesgetrandom(), you can see it for yourself in the sourceIf you use getentropy() or getrandom(), the interface actually blocks until the it has been initialized, on all OSs. It starts to return data the same time /dev/random starts to return data the first time.
If you use /dev/urandom on Linux, it reads wether it’s initialized or not. If you read /dev/random on Linux, it blocks until it’s initialiazed, and then says you remove entropy once you read anything, which doesn’t make sense with the current PRNG in the kernel (it might have with the old one). Both /dev/random and /dev/urandom on Linux really are broken. And they don’t fix /dev/urandom because it caused a hang in some applications (which now hang anyway because they switched to getrandom()).
If you can’t use getrandom()/getentropy(), you can work around /dev/urandom’s broken behaviour by reading 1 byte from /dev/random. This 1 byte only needs to be read by 1 application during the boot process, not every application should be doing that. Linux distro’s could fix this by adding that 1 byte read somewhere in the boot sequence, but none do because the boot would hang. We could do this in OpenSSL, or for FIPS we could just document that it’s up to the OS to make sure this has been done. This is the only problem FIPS has with the use of /dev/urandom, just like everybody else.
Recent Linux kernels actually are able to initialize the RNG within a few seconds of booting.
On some other OSs, /dev/random and /dev/urandom are actually sane, there is no difference between them, and only return data after it’s been initialized.
In short, just switch to getrandom()/getentropy().
Idea: instead of actually reading 1 byte from /dev/random, we could select /dev/random for readable, and once it is readable, read /dev/urandom. That would work more or less around the issues with concurrent access.
I’m pretty sure that this won’t guarantee that /dev/urandom has been properly seeded.
NIST doesn’t say anything about
getrandom()yet. However, after discussions with our FIPS lab, it appears that it would be a justifiable source and it is guaranteed to be seeded properly.MAYBE we should only read 1 byte from there and after that continue with /dev/urandom. I observe that once one byte is available several (~8) one byte reads succeed immediately untit the /dev/random starts to block again. As a compromise ?
According to https://github.com/openssl/openssl/pull/8023#discussion_r256318746 You may assume that randBytes is returning error when not enough entropy, and when you read from the randBytes the pool starts to get empty again, it depends on the hardware how long this takes, can be seconds or minutes. Is it better to wait or to fail if this happens?
Yes, but and that is only after the
getentropyedit: getrandom syscall which should have been used, but maybe the fallback after the mis-configuration is wrong, and should use /dev/random even if it waits.Just wait until the initial seeding has completed and then simply use it’s output without blocking any further (that’s what
getrandom()does).In this context you might find the following blog post interesting:
https://www.2uo.de/myths-about-urandom
The author’s opinion might not be authoritative, but ~he~ it seems to be supported by D. J. Bernstein himself. And if I understand Pauli’s comment correctly, even the FIPS lab members agree.
The real problem is not that your process needs to wait longer, it’s that it slows down all other processes, too, which do the same. Just assume that libcrypto would seed from
/dev/randomby default. Then all processes using OpenSSL would seed from/dev/randomand draining the entropy pool. In the case of a web server, which forks for incoming connections, I guess you would notice the slow down a lot.You can simulate this draining effect using
dd. Just open one terminal runningand several terminals running
You will notice that the performance gets worse and worse if you add more and more of these.
Finally, compare it to the performance of reading from
/dev/urandom:It is OS business to ensure that the kernel RNG is properly seeded with entropy on system boot, openssl has no way to influence/enforce that (apart from using getrandom). To summarize - use getrandom/getentropy (without GRND_RANDOM flag of course) and if it is not available, use /dev/urandom. That’s it.
Please do not ever use /dev/random in openssl by default. That would be simply not usable in any normal scenario where openssl is used throughout the system.
According to the discussions I’ve had with the FIPS lab about entropy sources, this isn’t correct. Calling a blocking
getrandom()system call is good enough for FIPS and is straightforward to justify.If we need to use /dev/urandom, the only method I can think of to guarantee that it is properly seeded is via waiting for something like
dd if=/dev/random of=/dev/urandom BS=32 count=1 iflag=fullblockI.e. read bytes from the blocking source and feed them back into the RNG.We should avoid /dev/random is we can, but we might not be able to do so.
getrandom() and getentropy() will always block until the kernel CSPRNG has been initialized. If you have a kernel that supports it, so >= 3.17, we should always use it. Only with an older kernel should be fall back to using /dev/urandom.
I’ve actually had people report that the boot now hangs because of this for some people, for instance openssh blocking until it can get random data.
I don’t think we should be using /dev/random at all. If getrandom()/getentropy() is not available, /dev/random will probably also do the wrong thing and cause things to hang, but then hang all the time.
Reading 1 byte from /dev/random should also not be done by each process. People that want to make sure that no application reads from /dev/urandom before it’s initialized should prevent those applications from starting instead, by for instance reading 1 byte during the boot process.
I still don’t think we should do this, we would hog the entropy pool by doing it. Just think of the OpenSSL testsuite where a lot of
opensslinstances are launched. If they all would be seeding from/dev/random(orgetentropy()), you would be in trouble.For the current Linux kernels the fact that
getrandom()blocks until the initial seeding has completed should be sufficient. After that, the kernel CSPRNG is cryptographically secure, which is good enough, unless we are running in FIPS 3.0 mode (t.b.d.) and it is either the first seeding of the<master>DRBG, or prediction resistance was requested. (It is clear that the entropy gathering needs to be improved for FIPS and that we need distinction between blocking sources with entropy guarantee and nonblocking sources).As for older kernels without
getrandom(): IMHO it is the job of the init system to save and restore a seed file across reboots, as recommended in the citation from RANDOM(4). So I wouldn’t consider it an OpenSSL bug if this message occurs.So, how about that?
The point is, if we use /dev/urandom to initialize the DRBG the kernel tells us that the returned random data is not good enough. I think we should probably use /dev/random, and wait for enough entropy.