81
resulting bit would have a 50 chance of being 0, and a 50 chance of being 1. The output can be said to have one bit of entropy. We can also say that the value of the bit is truly random. If the
coin flip was not fair, then we would have less than a bit of entropy, indicating that the resulting output isnt truly random.
It is difficult for a deterministic machine like a computer to produce true entropy. Often, entropy is collected in small bits from all sorts of unpredictable events such as the low-order bits of the time
between keystrokes, thread exits, and hard-disk interrupts. Its hard to determine how much entropy actually exists in a piece of data, though. Its fairly common to overestimate how much
entropy is available.
In general, entropy is unpredictable data, whereas pseudorandom numbers generated by a PRNG are not unpredictable at all if both the algorithm and the seed are known. Aside from using
entropic data to seed the PRNG, its also a good idea to use pure entropy for generating important keys. If we generate a 256-bit key using a pseudorandom number generator that has a 128-bit seed,
then our key does not contain 256-bits of strength, despite its length. At most, it has 128 bits. Similarly, if multiple keys are generated using the same seed, there will be correlations between
the keys that are undesirable. The security of the keys should be independent.
For all other random number requirements, pseudorandom numbers generated by the PRNG are suitable for use.
4.4.1 Seeding the PRNG
A common security pitfall is the incorrect seeding of the OpenSSL PRNG. There are functions that seed the generator easily enough, but the problems occur when a developer uses some
predictable data for the seed. While the internal routines can quantify the amount of seed data, they can do nothing to determine the quality of that data i.e., how much entropy the data contains.
Weve stated that the seed is an important value, but we havent explicitly looked at why this is so. For example, when using a session key to secure a connection, the basis for security is both the
encryption algorithm used to encrypt the messages and the inability of the attacker to simply guess the session key. If an insecure seed is used, the PRNG output is predictable. If the output is
predictable, the keys generated are predictable; thus the security of even a correctly designed application will be compromised. Clearly, a lot depends on the PRNGs output and as such,
OpenSSL provides several functions for manipulating it. Its important to understand how to use these functions so that security can be assured.
The function
RAND_add
seeds the PRNG with the specified data, considering only the specified number of bytes to be entropic. For example, suppose the buffer contained a pointer to the current
time as returned by the standard C runtime function,
time
. The buffer size would be four bytes, but only a single byte of that could be reasonably considered entropic because the high bytes dont
change frequently and are extremely predictable. The current time by itself is never a good source of entropy; weve only used it here for clarity.
void RAND_addconst void buf, int num, double entropy;
buf The buffer that contains the data to be used as the seed for the PRNG.
num The number of bytes contained in the buffer.
entropy
82
An estimate of the quantity of entropy contained in the buffer. Like
RAND_add
, the function
RAND_seed
seeds the PRNG with the specified data, but considers it to contain pure entropy. In fact, the default implementation of
RAND_seed
is simply to call
RAND_add
using the number of bytes in the buffer as the amount of entropy contained in the buffers data.
void RAND_seedconst void buf, int num;
buf The buffer that contains the data to be used as the seed for the PRNG.
num The number of bytes contained in the buffer.
Two additional functions are provided for use on Windows systems. Theyre not the best sources of entropy, but lacking a better source, theyre better than what most programmers would typically
use or devise on their own. In general, its a good idea to avoid using either of these two functions unless there is no other entropy source available, especially if your application is running on a
machine that ordinarily has no user interaction, such as a server. Theyre intended to be a last resort, and you should treat them as such.
int RAND_eventUINT iMsg, WPARAM wParam, LPARAM lParam; RAND_event
should be called from message handling functions and pass each messages identifier and parameters. The current implementation uses only the
WM_KEYDOWN
and
WM_MOUSEMOVE
messages for gathering entropy.
void RAND_screenvoid; RAND_screen
can be called periodically to gather entropy as well. The function will take a snapshot of the contents of the screen, generate a hash for each scan-line, and use the hash value
as entropy. This function should not be called too frequently for a couple of reasons. One reason is that the screen wont change much, which can lead to predictability. The other reason is that the
function is not particularly fast.
A common misuse of the PRNG seeding functions is to use a static string as the seed buffer. Most often, this is done for no reason other than to silence OpenSSL because it will generate warning
messages whenever the PRNG is not seeded and an attempt to use it is made. Another bad idea is to use an uninitialized memory segment, assuming its contents will be unpredictable enough.
There are plenty of other examples of how not to seed the PRNG, but rather than enumerate them all here, well concentrate on the right way. A good rule of thumb to determine whether youre
seeding the PRNG correctly is this: if youre not seeding it with data from a service whose explicit purpose is to gather entropy, youre not seeding the PRNG correctly.
On many Unix systems, devrandom is available as an entropy-gathering service. On systems that provide such a device, there is usually another device, devurandom. The reason for this is that
the devrandom device will block if there is not enough entropy available to produce the output requested. The devurandom device, on the other hand, will use a cryptographic PRNG to assure
that it never blocks. Its actually most accurate to say that devrandom produces entropy and that devurandom
produces pseudorandom numbers.
83
The OpenSSL package provides a function,
RAND_load_file
, which will seed the PRNG with the contents of a file up to the number of bytes specified, or its entirety if the limit is specified as -
1. It is expected that the file read will contain pure entropy. Since OpenSSL has no way of knowing whether the file actually does contain pure entropy, it assumes that the file does;
OpenSSL leaves it to the programmer. Example 4-9
shows some example uses of this function and its counterpart,
RAND_write_file
. On systems that do have devrandom available, seeding the PRNG with
RAND_load_file
from devrandom is the best thing to do. Be sure to limit the number of bytes read from devrandom to some reasonable value, though If you specify -1 to
read the entire file,
RAND_load_file
will read data forever and never return. The
RAND_write_file
function will write 1,024 bytes of random bytes obtained from the PRNG to the specified file. The bytes written are not purely entropic, but they can be safely used
to seed an unseeded PRNG in the absence of a better entropy source. This can be particularly useful for a server that starts running immediately when a system boots up because devrandom
will not have much entropy available when the system first boots. Example 4-9
demonstrates various methods of employing
RAND_load_file
and
RAND_write_file
.
Example 4-9. Using RAND_load_file and RAND_write_file
int RAND_load_fileconst char filename, long bytes; int RAND_write_fileconst char filename;
Read 1024 bytes from devrandom and seed the PRNG with it RAND_load_filedevrandom, 1024;
Write a seed file RAND_write_fileprngseed.dat;
Read the seed file in its entirety and print the number of bytes obtained
nb = RAND_load_fileprngseed.dat, -1; printfSeeded the PRNG with d bytes of data from prngseed.dat.\n,
nb;
When you write seed data to a file with
RAND_write_file
, you must be sure that youre writing the file to a secure location. On a Unix system, this means the file should be owned by the user ID
of the application, and all access to group members and other users should be disallowed. Additionally, the directory in which the file resides and all parent directories should have only
write access enabled for the directory owner. On a Windows system, the file should be owned by the Administrator and allow no permissions to any other users.
One final point worth mentioning is that OpenSSL will try to seed the PRNG transparently with devurandom
on systems that have it available. While this is better than nothing, its a good idea to go ahead and read better entropy from devrandom, unless there is a compelling reason not to.
On systems that dont have devurandom, the PRNG will not be seeded at all, and you must make sure that you seed it properly before you attempt to use the PRNG or any other part of OpenSSL
that utilizes the PRNG. For systems that have devrandom, Example 4-10
demonstrates how to use it to seed the OpenSSL PRNG.
Example 4-10. Seeding OpenSSLs PRNG with devrandom
int seed_prngint bytes {
if RAND_load_filedevrandom, bytes return 0;
return 1; }
84
4.4.2 Using an Alternate Entropy Source