142 lines
6.5 KiB
Plaintext
142 lines
6.5 KiB
Plaintext
My Random number library.
|
|
|
|
These routines can be used to generate pseudo random numbers and can be
|
|
used to 'seed' the pseudo random number generator (RNG). The RNG make no
|
|
effort to reproduce the same random number stream with each execution.
|
|
Various other routines in the SSLeay library 'seed' the RNG when suitable
|
|
'random' input data is available. Read the section at the end for details
|
|
on the design of the RNG.
|
|
|
|
void RAND_bytes(
|
|
unsigned char *buf,
|
|
int num);
|
|
This routine puts 'num' random bytes into 'buf'. One should make
|
|
sure RAND_seed() has been called before using this routine.
|
|
|
|
void RAND_seed(
|
|
unsigned char *buf,
|
|
int num);
|
|
This routine adds more 'seed' data the RNG state. 'num' bytes
|
|
are added to the RNG state, they are taken from 'buf'. This
|
|
routine can be called with sensitive data such as user entered
|
|
passwords. This sensitive data is in no way recoverable from
|
|
the RAND library routines or state. Try to pass as much data
|
|
from 'random' sources as possible into the RNG via this function.
|
|
Also strongly consider using the RAND_load_file() and
|
|
RAND_write_file() routines.
|
|
|
|
void RAND_cleanup();
|
|
When a program has finished with the RAND library, if it so
|
|
desires, it can 'zero' all RNG state.
|
|
|
|
The following 3 routines are convenience routines that can be used to
|
|
'save' and 'restore' data from/to the RNG and it's state.
|
|
Since the more 'random' data that is feed as seed data the better, why not
|
|
keep it around between executions of the program? Of course the
|
|
application should pass more 'random' data in via RAND_seed() and
|
|
make sure no-one can read the 'random' data file.
|
|
|
|
char *RAND_file_name(
|
|
char *buf,
|
|
int size);
|
|
This routine returns a 'default' name for the location of a 'rand'
|
|
file. The 'rand' file should keep a sequence of random bytes used
|
|
to initialise the RNG. The filename is put in 'buf'. Buf is 'size'
|
|
bytes long. Buf is returned if things go well, if they do not,
|
|
NULL is returned. The 'rand' file name is generated in the
|
|
following way. First, if there is a 'RANDFILE' environment
|
|
variable, it is returned. Second, if there is a 'HOME' environment
|
|
variable, $HOME/.rand is returned. Third, NULL is returned. NULL
|
|
is also returned if a buf would overflow.
|
|
|
|
int RAND_load_file(
|
|
char *file,
|
|
long number);
|
|
This function 'adds' the 'file' into the RNG state. It does this by
|
|
doing a RAND_seed() on the value returned from a stat() system call
|
|
on the file and if 'number' is non-zero, upto 'number' bytes read
|
|
from the file. The number of bytes passed to RAND_seed() is returned.
|
|
|
|
int RAND_write_file(
|
|
char *file),
|
|
RAND_write_file() writes N random bytes to the file 'file', where
|
|
N is the size of the internal RND state (currently 1k).
|
|
This is a suitable method of saving RNG state for reloading via
|
|
RAND_load_file().
|
|
|
|
What follows is a description of this RNG and a description of the rational
|
|
behind it's design.
|
|
|
|
It should be noted that this RNG is intended to be used to generate
|
|
'random' keys for various ciphers including generation of DH and RSA keys.
|
|
|
|
It should also be noted that I have just created a system that I am happy with.
|
|
It may be overkill but that does not worry me. I have not spent that much
|
|
time on this algorithm so if there are glaring errors, please let me know.
|
|
Speed has not been a consideration in the design of these routines.
|
|
|
|
First up I will state the things I believe I need for a good RNG.
|
|
1) A good hashing algorithm to mix things up and to convert the RNG 'state'
|
|
to random numbers.
|
|
2) An initial source of random 'state'.
|
|
3) The state should be very large. If the RNG is being used to generate
|
|
4096 bit RSA keys, 2 2048 bit random strings are required (at a minimum).
|
|
If your RNG state only has 128 bits, you are obviously limiting the
|
|
search space to 128 bits, not 2048. I'm probably getting a little
|
|
carried away on this last point but it does indicate that it may not be
|
|
a bad idea to keep quite a lot of RNG state. It should be easier to
|
|
break a cipher than guess the RNG seed data.
|
|
4) Any RNG seed data should influence all subsequent random numbers
|
|
generated. This implies that any random seed data entered will have
|
|
an influence on all subsequent random numbers generated.
|
|
5) When using data to seed the RNG state, the data used should not be
|
|
extractable from the RNG state. I believe this should be a
|
|
requirement because one possible source of 'secret' semi random
|
|
data would be a private key or a password. This data must
|
|
not be disclosed by either subsequent random numbers or a
|
|
'core' dump left by a program crash.
|
|
6) Given the same initial 'state', 2 systems should deviate in their RNG state
|
|
(and hence the random numbers generated) over time if at all possible.
|
|
7) Given the random number output stream, it should not be possible to determine
|
|
the RNG state or the next random number.
|
|
|
|
|
|
The algorithm is as follows.
|
|
|
|
There is global state made up of a 1023 byte buffer (the 'state'), a
|
|
working message digest ('md') and a counter ('count').
|
|
|
|
Whenever seed data is added, it is inserted into the 'state' as
|
|
follows.
|
|
The input is chopped up into units of 16 bytes (or less for
|
|
the last block). Each of these blocks is run through the MD5
|
|
message digest. The data passed to the MD5 digest is the
|
|
current 'md', the same number of bytes from the 'state'
|
|
(the location determined by in incremented looping index) as
|
|
the current 'block' and the new key data 'block'. The result
|
|
of this is kept in 'md' and also xored into the 'state' at the
|
|
same locations that were used as input into the MD5.
|
|
I believe this system addresses points 1 (MD5), 3 (the 'state'),
|
|
4 (via the 'md'), 5 (by the use of MD5 and xor).
|
|
|
|
When bytes are extracted from the RNG, the following process is used.
|
|
For each group of 8 bytes (or less), we do the following,
|
|
Input into MD5, the top 8 bytes from 'md', the byte that are
|
|
to be overwritten by the random bytes and bytes from the
|
|
'state' (incrementing looping index). From this digest output
|
|
(which is kept in 'md'), the top (upto) 8 bytes are
|
|
returned to the caller and the bottom (upto) 8 bytes are xored
|
|
into the 'state'.
|
|
Finally, after we have finished 'generation' random bytes for the
|
|
called, 'count' (which is incremented) and 'md' are fed into MD5 and
|
|
the results are kept in 'md'.
|
|
I believe the above addressed points 1 (use of MD5), 6 (by
|
|
hashing into the 'state' the 'old' data from the caller that
|
|
is about to be overwritten) and 7 (by not using the 8 bytes
|
|
given to the caller to update the 'state', but they are used
|
|
to update 'md').
|
|
|
|
So of the points raised, only 2 is not addressed, but sources of
|
|
random data will always be a problem.
|
|
|