In a GNU/Linux distributions, there are special files (/dev/random and /dev/urandom) to access the kernel’s random number generator. But how do we get random numbers from them in a bash script?

If we try to cat them we get random things:

$ cat /dev/random
XB>7;Mq'\'$,dH՞ՈT=H}0]eS19Ѡ
Hd9)7ܢVO'y.ۧMW^C

Something a bit weirder happens with /dev/urandom: it continues vomiting characters really fast at you until you stop it with Ctrl + C.

What’s the difference between /dev/random and /dev/urandom? Lets check the manpage for random (4), as of Linux 5.6:

   The  random number generator gathers environmental noise from device driv‐
   ers and other sources into an entropy pool.  The generator also  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/urandom device returns random bytes using a pseudoran‐
   dom number generator seeded from the entropy pool.  Reads from this device
   do not block (i.e., the CPU is not yielded), but can incur an  appreciable
   delay when requesting large amounts of data.

   The  /dev/random  device  is a legacy interface which dates back to a time
   where  the  cryptographic  primitives  used  in  the   implementation   of
   /dev/urandom  were  not  widely trusted.  It will return random bytes only
   within the estimated number of bits of fresh noise in  the  entropy  pool,
   blocking if necessary.  /dev/random is suitable for applications that need
   high quality randomness, and can afford indeterminate delays.

   When the entropy pool is empty, reads from /dev/random  will  block  until
   additional  environmental  noise  is  gathered. (...)

From this, we get that:

  • the Kernel maintains a pool of entropy from random things that happens in the computer.
  • both special files use this entropy pool to seed a PRNG.
  • /dev/random waits until there are enough randomness to output something.
  • /dev/urandom is preferred.
  • both files gives us bytes.

And from the Kernel’s source we get that urandom uses ChaCha20 as CSPRNG. 1

We now need a tool to read a specific number of bytes from those files and convert it to something human readable. And here comes od, a Unix command to display data in several formats.2 The default is octal, hence the name od: octal dump.

With this utility we can read N bytes from a file and display it in a specified format. For example, here’s the dump of `Hello World“:

$ echo Hello World | od
0000000 062510 066154 020157 067527 066162 005144
0000014

The first column is the offset, and then the octal representation of that line. So, the second line of the output means: starting at the offset 0000014 we have nothing, i.g. there are 14 (in octal, or 12 in decimal) characters in that string.

It looks like od does not know how to count. There are 11 chars in Hello World! Lets see each char individually (-c) in octal (-b):

$ echo "Hello World" | od -cb
0000000   H   e   l   l   o       W   o   r   l   d  \n
        110 145 154 154 157 040 127 157 162 154 144 012
0000014

Sweet, it counted the line ending as well.

od can also read N bytes from a source and display it in a specified format. So, to read 4 bytes from /dev/urandom, display it as an unsigned integer without the offset column:

$ od -vAn -N4 -t u4 < /dev/urandom
  402803061

And to use in your bash code:

for i in $(seq 1 10)
do
	random=$(od -vAn -N4 -t u4 < /dev/urandom)
	echo $random
done

With this code, those 32 bits (4 bytes) from urandom are interpreted as an integer. How do we get floats? If interested only in the range [0, 1): simply divide by the greatest possible value, \( 2^{32} - 1\).


Footnotes: