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