Using node.js crypto library I can generate cryptographically strong bytes. How can I use this to generate a random number between 0 and 9999 (both inclusive)?
2 Answers
I would personally recommend this simple procedure for selecting random values from 0 to N:
Step 1: ask your cryptographically secure RNG for a sample M that is large enough to cover the interval between 0 and N
In your case, if your CSRNG outputs bytes, then you would ask it for 2 bytes; that gives you a random value M between 0 and 65535
Step 2: modulo reduce M by the smallest power of 2 that is larger than N
In your case, the smallest power of two larger than N is 16384; hence, you take M (the value between 0 and 65535), and modulo reduce that by 16384, giving you a value between 0 and 16383;
M = M % 16384;
(language note: the above assumes that the result will always be in the range of 0 through 16383; if the programming language does something funny with the modulo of negative numbers, you might either avoid negative numbers altogether, or handle negative numbers as a special case)
Step 3: check if the value M is in the range you want, that is, no more than N; if it is not, then go back to step 1
In your case, you're looking for values between 0 and 9999; if M is outside the range, we discard it, and go back to step 1.
if (M > 9999) goto step_1;
Step 4: return the value M
Properties of the above algorithm:
It is unbiased. The only nonobvious point behind this is step 2; by modulo reducing M by a power of 2, it remains an unbiased value (only with a smaller range). This step is effectively masking off the higher order bits; leaving the lower order bits undisturbed.
It is cryptographically secure (assuming that the original RNG used in step 1 was cryptographically secure); any method that can be used on the output of this algorithm to distinguish it from random could be used against the original source to distinguish that from random.
It is moderately efficient; we'll run steps 1 through 3 (at most) an expected 2 times for every generated value. Now, there are alternative algorithms that use fewer expected random bits from your rng, however (IMHO) those methods aren't as easy to get right, and wringing the last bit of efficiency is less important than getting a method that we are confident is good.

- 147,019
- 11
- 229
- 360
First, how not to do it. You could generate 2 bytes, treat it as a 16 bit number and compute it modulo 10000. That would give you a number between 0 and 9999, but would not be cryptographically secure. It would have bias. For example, using python random.randint(0,65535) % 10000, I generated 1,000,000 random numbers and plotted the following histogram:
The proper way to do it would be to generate 2 random bytes and treat them as a 16 bit number, say $x$. Then compute $x' = x\mod 16384$ (note $16384 = 2^{14}$, i.e., the next power of two just bigger than 10000). Then, if $x'$ is less than 10000, return that number, otherwise start over. Using this process gives the following histogram on 1,000,000 random numbers.

- 38,563
- 8
- 112
- 180
-
A more efficient approach is to take the problem modulo 65536 and divide by 6 when less than 60000, you will waste less entropy this way. This question is actually a duplicate of a couple crypto.SE questions, in particular http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string?rq=1 and http://crypto.stackexchange.com/questions/6175/how-to-best-obtain-bit-sequences-from-throwing-normal-dice?lq=1 – Thomas Jun 24 '13 at 16:07
randomBytes
andpseudoRandomBytes
are named terribly.randomBytes
is actually pseudo-random (and cryptographically secure) whilepseudoRandomBytes
is not cryptographically secure? – Reid Jun 24 '13 at 14:42