In short: Instead of another question asking about when to use
/dev/random instead of
/dev/urandom, I present the following scenario, in which I find myself in an application I'm building:
- A VM or container environment (ie, a fresh install, probably only seconds old when the application is run for the first time)
- A need for cryptographically secure random bytes to use as keying material for the rest of the life of the installation (months or more)
- A user story and interface in which blocking (even for minutes, if necessary) is acceptable
I'm wondering: is this the rare but proper use case for a blocking random source (ie, using
getrandom with the blocking mode flag)?
/dev/random is a topic that has led to contentious discussion. For my part, I'm of the mind that
/dev/urandom is preferable in nearly all typical use cases - in fact, I have literally never used a blocking random source before.
In this popular and wonderful answer, Thomas Pornin makes the case that the
urandom man page is somewhat misleading (agreed) and that, once properly seeded, the
urandom pool will not "run out" of entropy in any practical scenario - and this comports with my understanding as well.
However, I think that he slightly oversells
urandom by saying that "the only instant where
/dev/urandom might imply a security issue due to low entropy is during the first moments of a fresh, automated OS install."
My understanding is that "boot-time entropy hole" for a typical Ubuntu server boot is over a minute long! This is based on research at the University of Michigan by J. Alex Halderman.
Halderman also seems to say that the entropy pool fills on each boot, and not, as Pornin says in his answer, at the very first OS install. Although it's not terribly important for my application, I'm wondering: which is it?
I have read the "Myths about Urandom" post by Thomas Hühn, but I find it unconvincing for several reasons, most pertinent for my application is that the post essentially boils down to "people don't like to be stopped in their ways. They will devise workarounds, concoct bizarre machinations to just get it running." While this is undoubtedly true (and is the reason I've always used
/dev/urandom everywhere else, especially for web stuff), there are some applications in which users will tolerate having to wait, especially if they are installing it for the first time.
I am building an application meant to be run locally in a terminal setting and I already have reason to create an expectation that the initial installation process will be a bit involved. I have no qualms about asking the user to wait a bit if it can add even a small amount of robustness against a repeated keypair.
In fact, Halderman says he was able to compute private keys for 105,728 SSH hosts - over 1% of those he scanned - because of weak entropy pools being used to generate the keypair. In this case, it was largely embedded devices which presumably have abysmal sources of entropy and thus a hard time filling their pool.
But - and this is perhaps the heart of my question - in an age when apps are shipped in wholly naive containers, meant to be run as if on a shiny, fresh OS installation only seconds old, aren't we reasonably concerned about this same phenomenon? Don't we need a practical blocking interface? And is that what
getrandom is intended to become?
Of course it's possible in many situations to share entropy from the host to the guest. But for the purposes of this question, let's assume that the author has decided not to do that, either because she won't have sufficient control over the particulars of the deployment or because there is no entropy pool available on the host.
Thinking just a bit further down the road: what are the best practices for environments which are as fresh and naive as I've described above, but which run on devices of fairly abysmal prospects for initial entropy generation? I'm thinking small embedded devices with few or no HIDs and which are perhaps air-gapped for the initial installation process.
edit: Update: So it appears that, as of PEP524, Python (in which the app in question is written) uses
os.urandom is called, and blocks in the event that the entropy pool hasn't gathered at least 128 bits. So as a practical matter, I think I have my answer - just use
os.urandom and it will behave like
/dev/random only when necessary. I am, however, interested in the over-arching question here (ie, does the era of containerization mean a re-thinking of "always just use urandom" orthodoxy).