Better Random Number Generation for OpenSSL, Libc, and Linux Mainline
aws.amazon.com
aws.amazon.com
http://www.metzdowd.com/pipermail/cryptography/2017-November...
Of course you could also write a security focused heap allocator that uses these madvise() options under the hood.
And yes I know CloudFlare did the lava lamps thing, like a bunch of other people did it before them. But it would be totally inline with Amazon to have a random numbers as a service service to compete with random.org.
It said "better random numbers" so I was hoping for something better than KMS random numbers.
>" Firstly, as mentioned, s2n uses the so-called “Prediction resistant” mode, which means that s2n feeds the random number generator with more entropy (from hardware) on every call, as long as the right hardware is available"
Would this be the RDRAND instruction or something else?
https://d2908q01vomqb2.cloudfront.net/ca3512f4dfa95a03169c5a...
Basically when the DRBG is initialized we start with /dev/urandom and the personalization string. That sets up the initial state, and then hardware entropy is "mixed in" every time the generator is used. Because the mixing in happens before AES is applied, that means that even if either stream of entropy is biased, AES will randomize it anyway (or else AES is a broken cipher!). That means that even if you controlled both inputs, it would still require at least as much computational work as it takes to compute AES to be able to generate a predictable output. That's a nice property of the DRBG construction.
I tried to search for this csprng in glibc, but I wasn't able to find anything. Can anyone provide more info?
So we still need to detect forks. Not sure this is entirely the silver bullet this makes it out to be. Maybe fork() is just a bad idea, for any number of reasons.
The option can be used to robustly reset a guard variable, and hence re-initialize an RNG, DRBG, PRF, or other generator where duplicate state across processes would be a security issue.
A rng-per-thread combined with initialization check before and after might cover all the cases I think...
But re-entrancy might be a problem ... e.g. if some crazy application writer has called fork() from a signal handler, and that happened right between those lines. For a case like this, I think moving the guard to post-generation (but pre-use) and using an atomic CAS option to have the guard effect the return value of the generator, can do the trick at the cost of having to very occasionally throw generated data away. But I haven't tried it on all platforms.
With a thread-local RNG (and a thread-local page which gets zeroed on fork) it sounds like you're probably best off extracting data from the RNG and then checking the flag to see if you need to go back and try again. The only cost of doing it this way is a very marginal extra cost whenever you need to regenerate entropy -- but you can think of that as just being a slight increase in the cost of the fork.
The only other question which comes to mind is whether you need to worry about breakage on systems which have weaker-than-x86 cache coherency. I'm 99% certain that you're fine, but I'd prefer to be 100% certain.
I assume you'd like to see this functionality added to other platforms as well?
I'm not sure what you mean by "arc4random isn't marked signal safe" -- you mean it can't be used in any process which receives signals?
[at least, that's my understanding]
https://github.com/awslabs/s2n/blob/master/utils/s2n_random....
(And to be clear, I mean "goto fail" as in "I'm sure it's correct, but it's also very very difficult to tell")
Somehow that seems pretty fringe.
I think we have a high bar for a code readability and that the code isn't screaming goto fail. Though we do hate ifdef's and goto's and that file is one of the few places we have any, again precisely because of the ickyness of the challenge prior to 4.14. But if it helps reassure you, we also have a test case to make sure that the fork detection works:
https://github.com/awslabs/s2n/blob/master/tests/unit/s2n_ra...
The test checks that a child and a parent really do produce different random streams.
When we're writing a crypto library, like s2n, or OpenSSL, we don't get a choice to avoid fork() ... the application can fork() any time it wants and we just have to be able to deal with it. There have been a few approaches:
* Always feed new entropy on every call (but this isn't always available)
* Use getpid() to detect a fork() ... but this can break because some versions of libc cache the value of getpid(). Another problem is the grand-child problem, where an application can fork(), have have the child start a new process group, then the parent exits, then the child fork()s again ... in that situation there's some chance that the grandchild ends up with the same PID as the original process that started everything (and may have used the RNG).
* To fix that, one clever approach that BoringSSL used for a while was to check both getpid() and getppid(), which signaficantly reduced the likelihood of occurrence but was still probabilistic.
* All sorts of weird clone() flags, that things like language runtimes and virtual machines use, can bypass pthread_atfork and PID changes.
Right now, the <4.14 fallback seems strictly worse.
(Disregarding that this is mostly academic, and that whoever is calling fork() while using your library is unlikely to have spent the same attention to detail when it comes to stuff like ephemeral keys..)
At a same time these environments often use native crypto that's implemented in C, for performance, and so it all comes together. Of course the other mitigations present still work, and we're talking about a very very obscure and unlikely set of circumstances, but why leave even a tiny door open.
Related discussion: https://news.ycombinator.com/item?id=9636861
One thing I like about the MINHERIT_ZERO/MADV_WIPEONFORK approach is that it's basically just a low-cost cache-friendly memory lookup at run-time, the only expense is at fork time.