When you create random numbers, you need to ask yourself what those random numbers are for.
For example, if you wrote a first person shooter and wanted to generate some random bullet spread, then the randomness doesn't need to be of high entropy or uniformly distributed, as long as it's "good enough" for the game.
On the other hand, if you were to write code that serves some kind of cryptographic purpose, then the random numbers you use have to fulfill certain properties, depending on their exact purpose. Back in 2008, the Debian team had a huge bug in their OpenSSL package, causing the quality of the random numbers to decrease significantly. Ironically enough, this was all due to a code quality tool complaining.
What is a nonce?
You mentioned that you intend to use your random numbers as a nonce. It really depends on the exact purpose of the nonce, but a nonce should fulfill two properties:
- It should only be used once, hence the name "nonce".
- It should be impossible to predict.
The second property is really what your code is all about.
Why is System.Random
not okay?
The System.Random
class was designed to provide random-looking numbers quickly. These numbers are really not "high quality", and designed for something like a game, as I showed before.
An attacker, who is able to read enough random numbers generated by your system may be able to predict future numbers generated by the system. This may not be a problem in a game, but is very likely a problem if you look at your crypto code and imagine the attacker knowing all the nonces.
What to use instead?
The MSDN page for System.Random
recommends using System.Security.Cryptography.RNGCryptoServiceProvider
. As the name implies, it's designed to be used for cryptographic applications. Be aware if you are already using a third party crypto library, they may provide their own interface for generating secure random numbers, which may be more user-friendly and less prone to implementation errors.
Usually, when developers generate random numbers, they want them in a specific range, such as "Between 0 and 9999". This gives way to biasing errors, which, due to the way the modulo operator works, makes some values more likely than others.
But since all you care is unpredictability, you can make your life easy and generate a nonce in an easy-to-use size, which means just generating it byte-wise.
For example, the following code generates a nonce of the desired length:
private static RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
public static byte[] GenerateNonce(uint nonceLength)
{
byte[] nonce = new byte[nonceLength];
rngCsp.GetBytes(nonce);
return nonce;
}
Why is this code simpler than the others? Because the code you provided is designed to deal with the case I mentioned above, where biasing can be a problem. Since you can generate "nice" values, you will never have biasing issues and your code becomes much easier.
Is this nonce really secure? How long does it need to be?
Yes. Well, almost. An attacker can still guess the nonce and has a small chance of guessing it right. But it's the same as thinking of a number between 0 and 2^128-1 and not telling you what it is. You can still guess, but your chances of guessing it correctly are...small. Very small.
In fact, they become smaller the larger the nonce becomes. How long should a nonce be to make guessing infeasible? The general answer is it should have at least 80 bits of entropy, so 10 bytes at minimum. If you feel like you can deal with the overhead, you can go ahead and use 16 bytes (128 bits) and you should be good to go.