Improving the Quality of Randomness in Objective-C
I have a different solution. One that will satisfy everyone, I should hope. Every time you want to generate a new die roll, do it this way:
Present a progress bar, and prompt the user to shake the device.
- While waiting, filter the acceleration data suitably (some low pass IIR should do nicely) and look for spikes with a certain given magnitude. Count level changes using hysteresis. Show a progress bar that shows the amount of shaking.
- At the same time, feed the raw acceleration data to a suitable cryptographic hash function, say, SHA-2.
When the progress bar gets all the way to the right, play the sound of dice rolling and use the output of the hash function (256 bits) to generate the die values. This will not work for more than 59d20. You can also save the hash function state as input to the next die roll.
Here's what you tell those nerds: The die roll is in no way algorithmically predictable. The only information used in determining the value of the die roll is how you shake the device, which is true for real dice as well. In theory you could shake the device the same way twice, just as in theory a highly skilled gambler could roll real dice to make them come up the way he wants.
How to use the output: You have 256 bits of random data, and want to get die rolls.
struct bits {
unsigned data[8];
unsigned pos;
};
// Get next n bits, or -1 if out of entropy.
int get_bits(struct bits *b, int n);
// Roll an n-sided die, or -1 if out of entropy
int uniform(struct bits *b, int n)
{
int nbits, x;
for (nbits = 0; (1 << nbits) < n; ++nbits);
do {
x = get_bits(b, nbits);
if (x < 0)
return -1;
} while (x >= n);
return x + 1;
}
This function works by slicing off a few bits of entropy at a time for your die rolls. So for a d8, you slice of 3 bits and use the result. For a d20, you slice off 5 bits for a d32, and reroll if the result is greater than 20. If you happen to run out of entropy (unlikely, but possible) for a given die roll, then I suggest printing a "die jacked" message and asking the user to shake some more for the remaining dice.
Footnote: The probability that you'll run out of entropy is very low, unless you roll a large number of dice. 256 bits is plenty. It takes 24d20 before the chance of running out of entropy even gets to 1%.
It's not really an answer and is a commentary, but it became long and here it is.
I can envision one solution that would entail getting some (purportedly random) touchpad movements from the user,
Note that arc4random_stir()
reads from /dev/urandom
, see the man page. So it seeds itself with the "environment".
Or, buy a radioactive source and a Geiger-counter, connect it to the USB, and generate the random number based on the reading of the counter. Nuclear decay is quantum mechanically random.
Generates an array of cryptographically secure random bytes.
int SecRandomCopyBytes (
SecRandomRef rnd,
size_t count,
uint8_t *bytes
);
Apple Randomization Services Reference Docs
Or just use arc4random(), it is as close to random as can be noticed, it is automatically seeded from /dev/urandom.