Seeding random number generators (ctd):
looking for entropy
On the previous page, we introduced the problem of finding a
random number generator seed. For casual applications,
System.nanoTime() will generally return a value that has enough
unpredictable bits to seed a generator such as java.util.Random or even
the XORShift generator we also mentioned. Using
System.nanoTime() almost certainly won't be able to produce any of the 264-1 starting values
for the 64-bit XORShift generator, and almost certainly not with equal probability. But for a casual
game, it may be good enough.
For more serious applications, we need to find more bits of "true randomness" or entopy.
In a moment, we'll see that:
In Java, a practical way to access entropy is to
use the SecureRandom
class. In some cases we may be able to mix in extra entropy if it is available.
Using SecureRandom to read OS-provided entropy
In practice, modern operating systems help us solve the problem of obtaining
entropy.
Depending on the machine and environment, there are various sources
of entropy potentially available to software.
The OS generally "knows" where it can collect entropy from.
So it collects entropy
"as it goes along" and exposes it to the programmar via an API
call or similar. On Windows, this is generally the CryptGenRandom() API call;
on UNIX-like systems such as Linux, Solaris and Mac OS,
by reading from the /dev/random device file.
In Java, the SecureRandom class
then acts as a wrapper to this OS-provided entropy:
- the Java SecureRandom class effectively provides access to the
OS-generated entropy stream where available, also mixing it in with some (lower-quality) sources of
entropy, such as names of files in the temp directory;
- if your program can easily generate some extra entropy, it generally "does no harm"
to mix in this extra entropy, and SecureRandom provides a means to do this;
- then, SecureRandom can generate high-quality seeds (and indeed,
high-quality random numbers in general, albeit more slowly than with some other generators).
To obtain a series of bytes of entropy, we call SecureRandom.getSeed(),
having first created an instance of SecureRandom. By default, at least in
Sun's implementation, SecureRandom.generateSeed() is effectively a wrapper around the
natively available source of entropy, if one is available. If the generator we are seeding
allows us to use the byte array directly, then we're home and dry. If the generator requires,
say, a long value as its seed, we need to do a bit of conversion. In this
case, we can use a ByteBuffer to help us:
public long getLongSeed() {
SecureRandom sec = new SecureRandom();
byte[] sbuf = sec.generateSeed(8);
ByteBuffer bb = ByteBuffer.wrap(sbuf);
return bb.getLong();
}
..
long seed = getLongSeed();
Random ranGen = new XORShiftRandom(seed);
When should you use SecureRandom.generateSeed()?
In general, use SecureRandom.generateSeed() to seed "non-casual"
random number generators where it's important that the generator's starting point
is any of the possible values. Typical applications are cryptography,
simulations (and other scientific uses), or other applications where high-quality
randomness is important, such as gambling games where money is involved.
In general, you would use SecureRandom.generateSeed()
in conjunction with a high-quality generator. In fact, our example
of using it to seed a generator with a 264-1 period is a little unrealistic.
If we're happy with an XORShift generator, we'll generally be happy seeding it
from System.nanoTime().
The following should be borne in mind:
- in the worst case, the call SecureRandom.generateSeed() may block until
the required entropy has been generated (i.e. enough "random events" such as
mouse movements and arrival of disk/network packets have occurred to generate
the required entropy);
- you should consider bits of entropy as a "valuable, shared resource"— if you request
entropy faster than it is generated, then your application will block, and so potentially will
other applicatons also requesting entropy.
Usually these constraints aren't too much of a problem: a single high-quality generator seeded with
a few bytes of entropy should last the lifetime of a typical application.
Next: more about SecureRandom
On the next page, we look at the SecureRandom class
for random number generation, and typical cases of when to use it.
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.