As we've mentioned, one way of generating a random seed is to use a source message digest algorithm such as MD5 or HAVAL. As input, give it as much data as you can based on temporary state. This data might include the output of ps - efl, the environment variables for the current process, its PID and PPID, the current time and date, the output of the random number generator given your seed, the seed itself, the state of network connections, and perhaps a directory listing of the current directory. The output of the function will be a string of bits that an attacker cannot likely duplicate, but which is likely to meet all the other conditions of randomness you might desire.
The Perl program in Example 23.1 is an example of such a program. It uses several aspects of system state, network status, virtual memory statistics, and process state as input to MD5. These numbers change very quickly on most computers, and cannot be anticipated, even by programs running as supe- ruser on the same computer. The entropy (randomness) of these values is spread throughout the result by the hashing function of MD5, resulting in an output that should be sufficiently random for most uses.
Note that this script is an excellent method for generating Xauthority keys (see "X security" in Chapter 17), if you need them. Simply execute it with an argument of 14 (you need 28 hex characters of key) and use the result as your key.
#!/usr/bin/perl # # randbits -- Gene Spafford <[email protected]> # generate a random seed string based on state of system # # Inspired by a program from Bennett Todd ([email protected]), derived # from original by Larry Wall. # # Uses state of various kernel structures as random "seed" # Mashes them together and uses MD5 to spread around # # Usage: randbits [-n] [-h | -H ] [keylen] # Where # -n means to emit no trailing linefeed # -h means to give output in hex (default) # -H means hex output, but use uppercase letters # keylen is the number of bytes to the random key (default is 8) # If you run this on a different kind of system, you will want to adjust the # setting in the "noise" string to system-specific strings. Do it as another # case in the "if...else" and e-mail me the modification so I can keep a # merged copy. (Hint: check in your manual for any programs with "stat" in # the name or description.) # # You will need to install a version of MD5. You can find one in the COAST # achive at ftp://coast.cs.purdue.edu/pub/tools/unix # Be sure to include its location in the PATH below if it isn't in one of the # directories already listed. $ENV{'PATH'} = "/bin:/usr/bin:/usr/etc:/usr/ucb:/etc:" . $ENV{'PATH'}; # We start with the observation that most machines have either a BSD # core command set, or a System V-ish command set. We'll build from those. $BSD = "ps -agxlww ; netstat -s ; vmstat -s ;"; $SYSV = "ps -eflj ; netstat -s ; nfsstat -nr ;"; if ( -e "/sdmach" ) { $_ = "NeXT"; } elsif ( -x "/usr/bin/uname" || -x "/bin/uname") { $_ = `uname -sr`; } elsif ( -x "/etc/version" ) { $_ = `/etc/version`; } else { die "How do I tell what OS this is?"; } /^AIX 1/ && ( $noise = $BSD . 'pstat -afipSsT')|| /^CLIX 3/ && ( $noise = "ps -efl ; nfsstat -nr")|| /^DYNIX/ && ( $noise = $BSD . 'pstat -ai')|| /^FreeBSD 2/ && ( $noise = $BSD . 'vmstat -i')|| /^HP-UX 7/ && ( $noise = $SYSV)|| /^HP-UX A.09/ && ( $noise = $SYSV . "vmstat -s")|| /^IRIX(64)? [56]/ && ( $noise = $SYSV)|| /^Linux 1/ && ( $noise = "ps -agxlww ; netstat -i ; vmstat")|| /^NeXT/ && ( $noise = 'ps agxlww;netstat -s;vm_stat')|| /^OSF1/ && ( $noise = $SYSV . 'vmstat -i')|| /^SunOS 4/ && ( $noise = $BSD . 'pstat -afipSsT;vmstat -i')|| /^SunOS 5/ && ( $noise = $SYSV . 'vmstat -i;vmstat -s')|| /^ULTRIX 4/ && ( $noise = $BSD . 'vmstat -s')||| die "No 'noise' commands defined for this OS. Edit and retry!"; #### End of things you may need to modify require 'getopts.pl'; require 'open2.pl'; ($prog = $0) =~ s|.*/||; $usage = "usage: $prog [-n] [-h | -H] [keylength]\n"; &Getopts('nhH') || die $usage; defined($keylen = shift) || ($keylen = 8); die $usage if ($keylen =~ /\D/); die $usage if ($opt_H && $opt_h); die "Maximum keylength is 16 bytes (32 hex digits)\n" if ($keylen > 16); # Run the noise command and include whatever other state we # can conveniently (portably) find. @junk = times(); $buf = `$noise` . $$ . getppid() . time . join(`', %ENV) . "@junk" . `ls -lai`; # Now, run it through the md5 program to mix bits and entropy &open2('m_out', 'm_in', "md5") || die "Cannot run md5 command: $!"; print m_in $buf; close m_in; $buf = <m_out>; ($buf =~ y/a-f/A-F/) if $opt_H; print substr($buf, 0, 2*$keylen); print "\n" unless $opt_n;