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

Most upvoted comments

Guys, some of you seem a bit confused about what getentropy() and getrandom() are and how they relate.

  • getrandom() is a syscall
  • getentropy() is a library routine that uses getrandom(), you can see it for yourself in the source

If 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.

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.

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.

use /dev/random even if it waits.

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 ?

It would be good to have more detailed information about the VxWorks randlib in general and the randBytes() call in particular, before answering this question. Is there any official documentation available online or offline?

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 getentropy edit: 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.

Why is that a problem? You either wait until there’s enough entropy,…

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/random by default. Then all processes using OpenSSL would seed from /dev/random and 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 running

watch -n 1 cat /proc/sys/kernel/random/entropy_avail

and several terminals running

dd if=/dev/random bs=1 count=32768 | hexdump

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:

dd if=/dev/urandom bs=1 count=32768 | hexdump

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.

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).

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=fullblock I.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.

Okay, if the getrandom syscall is not available we seed from /dev/urandom and only if that is not available use /dev/random, but it should be other way round.

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 openssl instances are launched. If they all would be seeding from /dev/random (or getentropy()), 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?

diff --git a/e_os.h b/e_os.h
index 9c0888e..461015a 100644
--- a/e_os.h
+++ b/e_os.h
@@ -28,9 +28,9 @@
  * default, we will try to read at least one of these files
  */
 #  if defined(__s390__)
-#   define DEVRANDOM "/dev/prandom","/dev/urandom","/dev/hwrng","/dev/random"
+#   define DEVRANDOM "/dev/random","/dev/prandom","/dev/urandom","/dev/hwrng"
 #  else
-#   define DEVRANDOM "/dev/urandom","/dev/random","/dev/srandom"
+#   define DEVRANDOM "/dev/random","/dev/urandom","/dev/srandom"
 #  endif
 # endif
 # if !defined(OPENSSL_NO_EGD) && !defined(DEVRANDOM_EGD)

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.