[swift-evolution] [Proposal] Random Unification

Félix Cloutier felixcloutier at icloud.com
Tue Sep 26 11:26:09 CDT 2017



> Le 26 sept. 2017 à 07:31, Xiaodi Wu via swift-evolution <swift-evolution at swift.org> a écrit :
> 
> * The distinction to be made here is CSPRNGs versus non-cryptographically secure PRNGs, where CSPRNG : PRNG. “Reproducible” is not the right word. Based on my understanding, some CSPRNGs can be “reproducible” if the seed is known; what makes it cryptographically secure is that observing its previous *outputs* does not provide information useful to predict future outputs. Along those lines, it may be important to securely delete the seed from memory as soon as possible; there is some way of doing so in C (it’s used in the ChaCha20 reference implementation) but I don’t believe any way of doing so in Swift.

It's possible to use a CSPRNG-grade algorithm and seed it once to get a reproducible sequence, but when you use it as a CSPRNG, you typically feed entropy back into it at nondeterministic points to ensure that even if you started with a bad seed, you'll eventually get to an alright state. Unless you keep track of when entropy was mixed in and what the values were, you'll never get a reproducible CSPRNG.

We would give developers a false sense of security if we provided them with CSPRNG-grade algorithms that we called CSPRNGs and that they could seed themselves. Just because it says "crypto-secure" in the name doesn't mean that it'll be crypto-secure if it's seeded with time(). Therefore, "reproducible" vs "non-reproducible" looks like a good distinction to me.

> * On the issue of consuming entropy: a glaring underlying inconvenience in the API needs to be reckoned with. Sometimes, there simply isn’t enough entropy to generate another random number. If cryptographic security were not default, then it might be OK to fall back to some other method that produces a low-quality result. However, if we are to do the secure thing, we must decide whether the lack of entropy results in a call to a random method to (a) return nil; (b) throw; (c) fatalError; or (d) block. There is no way to paper over this problem; not enough entropy means you can’t get a random number when you want it. The debate over blocking versus non-blocking error, for example, held up the addition of getrandom() to Glibc for some time. In my proposed design, initializing a PRNG from the system’s secure stream of random bytes is failable; therefore, a user can choose how to handle the lack of entropy. However, it is desirable to have a thread-local CSPRNG that is used for calls, say, to Int.random(). It would be unfortunate if Int.random() itself was failable; however, that leads to an uncomfortable question: if there is insufficient entropy, should Int.random() block or fatalError? That seems pretty terrible too. However, one cannot simply write this off as an edge case: if this is to be a robust part of the standard library, it must do the “right” thing. Particularly if Swift is to be a true systems programming language and it must accommodate the case when a system is first booted and there is very little entropy.

That's not really the case anymore these days. You're probably thinking of /dev/urandom vs. /dev/random. On Darwin, they're the same thing and never run out (see man urandom). On Linux, the state of the art is that you leave /dev/random alone. Don't take it from me, Prof. Daniel J. Bernstein <https://en.wikipedia.org/wiki/Daniel_J._Bernstein> wrote this <https://www.mail-archive.com/cryptography@randombit.net/msg04763.html> a while ago:

> Think about this for a moment: whoever wrote the /dev/random manual page seems to simultaneously believe that
> 
>    (1) we can't figure out how to deterministically expand one 256-bit /dev/random output into an endless stream of unpredictable keys (this is what we need from urandom), but
> 
>    (2) we _can_ figure out how to use a single key to safely encrypt many messages (this is what we need from SSL, PGP, etc.).
> 
> For a cryptographer this doesn't even pass the laugh test.

So that shouldn't be too concerning.

> * What should the default CSPRNG be? There are good arguments for using a cryptographically secure device random. (In my proposed implementation, for device random, I use Security.framework on Apple platforms (because /dev/urandom is not guaranteed to be available due to the sandbox, IIUC). On Linux platforms, I would prefer to use getrandom() and avoid using file system APIs, but getrandom() is new and unsupported on some versions of Ubuntu that Swift supports. This is an issue in and of itself.) Now, a number of these facilities strictly limit or do not guarantee availability of more than a small number of random bytes at a time; they are recommended for seeding other PRNGs but *not* as a routine source of random numbers. Therefore, although device random should be available to users, it probably shouldn’t be the default for the Swift standard library as it could have negative consequences for the system as a whole. There follows the significant task of implementing a CSPRNG correctly and securely for the default PRNG.

Theo give a talk a few years ago <https://www.youtube.com/watch?v=aWmLWx8ut20> on randomness and how these problems are approached in LibreSSL.

Félix

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170926/5de1e883/attachment.html>


More information about the swift-evolution mailing list