blog

Filip Gospodinov

/dev/random vs /dev/urandom

Historically, there has been a lot of confusion about these two sources for random numbers and maybe there still is. /dev/random will block if there’s “not enough entropy” in the system while /dev/urandom will never block. Therefore, /dev/random has the reputation of being more secure. Truth is, more often than not /dev/random causes the system to hang for no good reason. Let’s see why.

TL;DR

Most programming languages already provide native APIs that do the right thing. Use them!

lang func
C getrandom()
C++ getrandom()
Rust rand::random()
Go import “crypto/rand”; rand.Read()
Python import secrets; secrets.choice()
Shell dd if=/dev/urandom …
Javascript crypto.getRandomValues(), crypto.subtle.generateKey()

If you cross-compile C/C++ to Windows/macOS/BSD then use a crypto library with a proper abstraction, maybe BoringSSL.

Mental model

A good way of thinking about a CSPRNG is to think of it as a stream cipher. Once it’s seeded properly it will output an infinite pseudorandom bit sequence. The weak spot of a CSPRNG is therefore the early stage after boot when there’s not enough entropy collected to properly seed the CSPRNG. Hence, /dev/random is only the right source of randomness during early boot (often it’s IOT devices that are initializing key material on first boot). In all other cases, the non-blocking /dev/urandom should be used.

The solution

Use the “new” getrandom() syscall. It will always do the correct thing unless the GRND_RANDOM flag is set.

The manpage knows

Everything is properly explained in man 7 random.

Unless you are doing long-term key generation (and most likely not even then), you probably shouldn’t be reading from the /dev/random device or employing getrandom(2) with the GRND_RANDOM flag. Instead, either read from the /dev/urandom device or employ getrandom(2) without the GRND_RANDOM flag. The cryptographic algorithms used for the urandomsource are quite conservative, and so should be sufficient for all purposes.

Also, the manpage maintains a table about blocking behavior of the API.

Interface Pool Blocking behavior Behavior when pool is not yet ready
/dev/random Blocking pool If entropy too low, blocks until there is enough entropy again Blocks until enough entropy gathered
/dev/urandom CSPRNG output Never blocks Returns output from uninitialized CSPRNG (may be low entropy and unsuitable for cryptography)
getrandom() Same as /dev/urandom Does not block once is pool ready Blocks until pool ready
getrandom() GRND_RANDOM Same as /dev/random If entropy too low, blocks until there is enough entropy again Blocks until pool ready
getrandom() GRND_NONBLOCK Same as /dev/urandom Does not block once is pool ready EAGAIN
getrandom() GRND_RANDOM + GRND_NONBLOCK Same as /dev/random EAGAIN if not enough entropy available EAGAIN

But I need entropy

No, you don’t. Once the CSPRNG is seeded properly you can rely on its output. If you don’t trust this causality then you also must distrust the integrity of stream ciphers. The CSPRNG is even superior because it’s constantly mixing in more entropy.

So many traps!

Despite all the effort to refute the myths around Linux random number sources they keep on persisting. There’s an effort to make /dev/random/ behave like /dev/urandom.

Further Resources

  1. https://www.2uo.de/myths-about-urandom/
  2. https://media.ccc.de/v/32c3-7441-the_plain_simple_reality_of_entropy

Written on Apr 13, 2020.