Random Number Generation In C and Bash

Well that was random! Or was it? Maybe it was pseudo-random…who knows? Random number generation is an important part of cryptography when seeding a hash or creating a cipher. But how can you generate a random number from a computer, if a computer is deterministic? This is where pseudo-random and random numbers come in. They are referred to as pseudo random because a computer is deterministic, however, the Linux kernel has implemented the /dev/random and /dev/urandom options for number generation that uses environmental noise from the hardware in your computer via the device driver of the device as a seed. Awesome, wikipedia states for /dev/random, which provides the ‘most’ random values:

In this implementation, the generator keeps an estimate of the number of bits of noise in the entropy pool. From this entropy pool random numbers are created. When read, the /dev/random device will only return random bytes within the estimated number of bits of noise in the entropy pool. /dev/random should be suitable for uses that need very high quality randomness such as one-time pad or key generation. When the entropy pool is empty, reads from /dev/random will block until additional environmental noise is gathered.

The /dev/urandom is non-blocking and thus will re-use the pool even with low entropy. There is also the C function call random() that can be used for pseudo-random number generation.

Lets get to some examples!

Using /dev/random For A Random Number

To grab a random number from /dev/random we simply do a read on the file handle into an unsigned integer and voila.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
#define RANDPATH "/dev/random"
 
int main()
{
	int fp, ret;
	unsigned int rand;
 
        // Open as read only
	fp = open(RANDPATH, O_RDONLY);
	if(fp < 0){
		fprintf(stderr, "Could not open -%s- for reading.\n",RANDPATH);
		return -1;
	}
 
	ret = read(fp, &rand, sizeof(rand));
        if(ret < 0){
                fprintf(stderr, "Could not read from -%s-.\n",RANDPATH);
                close(fp);
                return ret;
        }
 
	fprintf(stdout, "Random value: %u\n", rand % 10); // Range from 0 to 10
 
	close(fp);	
	return 0;
}

Easy as that, if you are planning to do many reads, take into account the /dev/random will block if the entropy is too low. If you require a large set of random numbers, use /dev/urandom but be aware you will retrieve more psuedo-random values than random.

Retrieve A Pseudo-Random Number With ‘random()’

Perhaps your kernel does not support /dev/random or /dev/urandom and you simply need a pseudo-random value. This can be achieved using the random() function call, providing an initial seed prior to calling random(). For example:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
 
int main()
{
	int j;
 
	srand((unsigned int)time(NULL));
 
	// Account for inherent flaws using Modulo use divisor instead
	j = 1 + (int)( 10.0 * random() / ( RAND_MAX + 1.0 ) );
 
	fprintf(stdout, "Random value: %u\n", j);
 
	return 0;
}

This will produce a random value between 1 and 10. If you require a larger value multiply random by 100 for 1 to 100 etc. srand() is used to seed the random number generator, in this case I pass in the current time as our seed. time() returns a value in seconds, if you need to retrieve a set of pseudo-random values quicker than once a second, pass in nanoseconds or microseconds as a new seed.

Generate A Random Number In Bash

In Bash we can utilize the /dev/random file handle, if present in your kernel, using hexdump to grab values as hexidecimal.

rand=$((0x$(hexdump -n 1 -ve '"%x"' /dev/random) % 100))

This will give us a value between 1 and 100. This can only be used for a 1 byte value (2^8), as our length is only 1 byte. If you require more, you must specify in hexdump as -n X where X is the length of bytes and update your modules.

There is also an internal random number that Bash populates, but provides a pseudo-random integer in the range 0 – 32767, and should never be used for encryption keys.
It is simply $RANDOM.

For example for a value between 0 – 32767

number=$RANDOM

You can also use modules to grab a value to a maximum size via $RANDOM % 100 for example.

Was that random after all?