Randomness extractors: making fair coins out of biased coins
bytepawn.com
bytepawn.com
Once you believe you piped enough you use the state of the cryptographic primitive as the seed for further random bit generation. The Linux kernel uses a sponge (to accumulate), hash function (to consolidate) and a stream cipher (to output) to 'convert' events with some degree of randomness into 'infinite' safe cryptographically secure random bits.
To acquire some intuition about this you can imagine taking a raw 1MP photo with a camera sensor and then feeding the lossless file to sha256sum. You acquire a 256 bit string and the sensor noise in the photo will be sufficient to secure the result. An attacker would need to model all the degrees of freedom in taking photos in the world and sensor noise production to build a simulator for your camera and start bruteforcing your sha256 result which will almost certainly (sensor might be compromised or not really be raw) contain far more degrees of freedom than 256 bits.
If I flip a coin n times and it comes up heads everytime, what's my best estimate of the likelihood of tails?
It came out as 1/2^(1/(n + 1)); and the chance of heads = (1 - that).
The calculus for results in between seemed intractable to me - or at least well beyond my abilities...
So I threw it into a newton-raphson solver and was happy to see that it came out pretty much linear (the most asymmetrical result will be the one for three trials, and since that was basically linear all results for greater n will be as well).
But I never went quite this far - for that you'd also need to calculate the standard deviation of the probability estimate (I don't think that it would've been much harder than what I did, but it was outside of my requirements at the time, so it was something I never implemented).
* coin always favours H or T. simple bias
* coin has some component of behaviour which can be on, off or reset. For example a liquid mercury component, which can bias the H or T outcome but the right kind of "flip" resets it to a known-safe mode so the coin has less to no bias.
* coin has bias which only manifests in skilled hands. a particular kind of flip.
The point I'm making is that probably, the bias is always assumed to be H or T favouring, but doesn't admit more complex coin bias where it could be 2 or more actors and 2 or more capable of biassing, and a pigeon who can't (or a dummy, and a pigeon: a good con generally has more people involved than you think)
I'll redo the maths and check.
∫(0 -> x)(p^n)dp = ∫(x -> 1)(p^n)dp
[p^(n+1)/(n+1)](0 -> x) = [p^(n+1)/(n+1)](x -> 1)
x^(n+1) - 0^(n+1) = 1^(n+1) - x^(n+1)
2 * x^(n+1) = 1
x^(n+1) = 1/2
x = 1/(2^(1/(n+1)))
Was I wrong? Have I been wrong for decades now?There are other probabilities that can lead to zero successes - every probability except for one.
And yes, they diminish rapidly as the number of trials goes up (much in the same way as it does in my equation).
> I was uncomfortable with the use of biases to assign non-zero probabilities to events that fail to occur after some number of trials.
Anyway, it entirely depends on your world view. When you say "There are other probabilities that can lead to zero successes" then that sounds like a Bayesian framework and that you have to pick your prior on the world. A natural choice is uniform (also Beta(1,1)) and update your prior as you collect data. You would then use the mean/median/mode of your posterior as your estimate for the bias. In your case, it appears you are operating in a Bayesian world but forcing your prior to be constantly uniform despite observing data. The frequentist perspective is that the probability is the relative frequency of observations. In this example, a frequentist would say that the probability of heads is 1 and tails is 0.
Got you now, thanks.
Take the case of zero successes, that can happen for every probability except for one.
The graph peaks at zero, but if you calculate 'x' such that the integral from zero to x of the binomial function is equal to the integral from x to one, you get a nice centre point.
You then throw out all signals that are not those two signals, and the conditional distribution will renormalise itself to give you a fair coin toss.
You pay for this by throwing out many bits that are not these two signals. The less fair the coin, the more coin flips you throw away.
In the trivial case of a fair coin, you throw away nothing and keep every coin toss. In a biased coin, you throw away any pairs of HH or TT.
Independence is a major assumption underlying any of these models.
Would love to see a proof of that. It feels a bit unintuitive to me.
(But on second thought that might be because I’m thinking of cases of correlated bits.)
If you define best as the most efficient at converting inputs to outputs as well as the output being 50/50 then there are better ways such as your example.
Re-reading this bit, I agree my wording is very clunky. What I meant by "whether we know the bias of the input bit stream or not" was:
[For p≠1/2] whether you know the value of p or not, this is the best approach. Of course, p=1/2 is the trivial case where we can just pass through..
HT xx -> H
TH xx -> T
HH TT -> H
TT HH -> TThanks for that, that's obviously better in terms of bitrate, I haven't thought of that. Any two bit sequences with equal probability can be used to emit either a 0 and 1..
[1] https://peteroupc.github.io/bernoulli.html
[2] Knuth, Donald E. and Andrew Chi-Chih Yao. “The complexity of nonuniform random number generation”, in Algorithms and Complexity: New Directions and Recent Results, 1976.
These techniques (eg von neumann) all seem to suffer from that same problem, namely, they crank out uniformly distributed random bits from biased sources, but with no guarantee of how long you may have to wait for a bit to come out.
And, as a matter of fact, much faster than the input if required.
This can be mitigated if you use a unkeyed hash function instead, but even for these we don't have any provable guarantees unless you assume the function is a random oracle, which is an ideal object that cannot exist.
https://en.wikipedia.org/wiki/Fortuna_(PRNG)
[EDIT]: Also: https://words.filippo.io/dispatches/linux-csprng/
Now, suppose that you had a function that turned N input bits with a 1:2 bias into a single bit with no bias. Then you could use this to implement an (0,1,2)-function as above, by collapsing inputs 1 and 2 into input 1. Since such an (0,1,2)-function is impossible, the 1:2-biased function is similarly impossible.
This argument holds for any i.i.d. input source that can be generated from a uniform-integer source with an odd number of possible values.
To generate a stream of random data, use a hash function with arbitrary-length output (XOF) such as blake2x[^0] or shake256[^1]. Make sure your key contains at least 256 bits of entropy. Absolutely never use a key with less than 128 bits of entropy.
Since it's impossible to know how much entropy there is in a key, you probably want to use something like the Fortuna RNG[^2]. Substitute the sha2/AES based construction for your XOF. Bruce Schneier designed Fortuna back when XOFs were harder to come by.
If you want more performance, you can use blake2 to compress your input seed into 256 bits and generate the random stream using chacha20[^3].
All of this is usually handled by the Linux kernel[^4], so it's best to just use the getrandom(2)[^5] system call or just read from /dev/urandom[^6]. If you are writing a Rust program, you can use the rand[^7] crate, which uses a mixed approach reading a seed from the operating system and expanding it in-process using chacha[^8]. This is a valid strategy.
I am omitting some subtleties[^10] about mathematical definitions of randomness extractors as used by the author of the article. When you are using a cryptographic approach, you are dealing with a complexity-theory based security notion[^9], which does not precisely equate to creating a stream with a specific amount of entropy. Everywhere – except for a physics or a math paper dealing with information theory – I would call this a technicality. For most intents and purposes, cryptographic security notions are the most real-world robust conceptions of randomness available.
[^0]: https://www.blake2.net/
[^1]: https://csrc.nist.gov/pubs/fips/202/final (shake256 is part of the SHA-3 standard)
[^2]: https://www.schneier.com/academic/fortuna/
[^3]: https://protonvpn.com/blog/chacha20
[^4]: https://lwn.net/Articles/884875/
[^5]: https://man.archlinux.org/man/getrandom.2
[^6]: https://man.archlinux.org/man/urandom.4
[^7]: https://crates.io/crates/rand
[^8]: https://docs.rs/rand/0.8.5/rand/rngs/struct.StdRng.html
[^9]: https://en.wikipedia.org/wiki/Security_of_cryptographic_hash...
[^10]: In particular, PRFs are not guaranteed to output tokens with a certain amount of entropy – if I recall correctly – because they can map two inputs to the same output.
---
I am the main author of the Rosenpass[^11] post-quantum secure key exchange for WireGuard. My expertise comes from developing this protocol, as well as a couple of years of engagement with the real-world cryptography community and from my own scientific research on cryptography and secure implementations of cryptography.
[^11]: https://rosenpass.eu/
Surely if you can just use a strong RNG to generate the key for the cryptographic algorithm you could just use that for all your randomness and ignore the stream of input entirely? The whole point of the article is how to extract the entropy from an unknown/untrusted input stream.
It's like the author has presented a recipe for a chocolate cake and you've said "it's better if you already have a cake, then you can just take a slice of that". Well yes.
Or in the domain of the article, faced with von Neumann's algorithm for getting fair flips from a biased coin, your solution amounts to "Instead I just flip my own coin which I know is fair."
Using hash functions requires a minimum amount of entropy in the seed. So do the schemes put forward in the article. In particular, these schemes require a relatively high degree of certainty about the amount of entropy in the stream at low variation. For the entropy extractors, the amount of total entropy required scales linearly with the length of the output stream. If you are using a hash function, the entropy requirement is constant.
The fact remains, both the method put forward in the post and using hash functions have a minimum entropy requirement. Even if you have only a small amount of entropy, hash functions will still get you more bang for the buck.
If push really comes to shove, you can still use a key stretching function[^0] to make it as hard as possible for an attacker to brute force what little entropy you have, as is routinely done with passwords.
To illustrate the difference in entropy requirement, imagine running the Von Neumann generator for a while, achieving the needed entropy level. In this scenario, the output stream of randomness will be fine. If you then get a section from the stream with very little entropy – much lower than the required amount – you get a section with very little entropy in your output stream. The Von Neumann generator can degrade, hash functions won't (for all practical intents and purposes).
Fortuna is designed to approach the entropy estimation problem. It is eventually secure even in the face of a determined attacker; due to the construction using entropy pools used with exponentially decreasing frequency of use, it covers an extreme range of different entropy levels – around ten orders of magnitude with 32 pools.
Crucially, once a secure level of entropy is reached, the RNG stays secure.
Of course, if the amount of entropy is low, Fortuna will produce quite a bit of insecure entropy before, eventually, producing secure entropy. In practice, this issue can be solved by running Fortuna for a while without producing any output. In a factory producing secure hardware devices, you might just allow the device to be active for a day, and you might some extra, high-quality entropy from the device that flashes your hardware devices in the first place.
Fortuna also allows you to use different sources of entropy securely. Using a Von Neumann generator for instance, achieving this is much harder.
Entropy estimation is borderline impossible in practice; Fortuna deals with this head on, with von Neumann you are lost if your estimate is off.