PHP, random numbers, and RDRAND CPU instruction
Published on 2015/04/11 by Igor Levicki
Why would you need to use RDRAND CPU instruction to generate random numbers in PHP when you can just call rand() function and be done with it? Well, take a look at this picture:
It has been generated by the following PHP code snippet which I borrowed from here (kudos to the original author):
<?php $img = imagecreatetruecolor(512, 512); $i = 0; while ($i < 512 * 512) { imagesetpixel($img, rand(0, 512), rand(0, 512), imagecolorallocatealpha($img, rand(0, 255), rand(0, 255), rand(0, 255), rand(0, 127))); $i++; } imagepng($img,"rand.png"); imagedestroy($img); ?>
As you can see, at least on the Windows platform, the PHP rand() function output doesn't look that random at all! This does not come as surprise, given that rand() function is most likely implemented using LCG (Linear Congruential Generator) whose period is only 232.
Luckily, PHP also has a mt_rand() function which is implemented using Mersenne Twister pseudo-random generator which has a very long period of 219937 - 1. Here is the picture of mt_rand() output:
Much better, right? It actually looks like random noise now!
So why do we need RDRAND instruction in PHP then? The honest answer is — we don't need it at all. Furthermore, my performance testing on Core i7-4770K shows that current RDRAND hardware implementation is 2.5 times slower than mt_rand() software implementation so it should be avoided in performance critical code.
Still, if you are curious, it is now possible to use it thanks to the PHP extension called php_hwrandom which I wrote in C some time ago. The extension exposes a single PHP function called hw_rand(), which has the same prototype as the built-in PHP rand() and mt_rand() functions. Here is the picture of hw_rand() output:
While it might not look much more random than mt_rand() to the naked eye, if you save the pictures to your computer and compare the PNG file sizes you will notice that the hw_rand.png file size is slightly larger, which confirms that the data generated by hw_rand() actually does have a slightly higher entropy.
Since the latest Apache and PHP binaries for Windows require Visual C++ Redistributable for Visual Studio 2012 Update 4, the php_hwrandom extension has been linked against the same runtime and thus has the same requirement. It goes without saying that your CPU must actually support the RDRAND instruction (Intel's Ivy Bridge was the first CPU to support it) if you want to use this PHP extension.
Both the Mersenne Twister based PRNG and the CPU hardware generate 32 bits of output, but due to the PHP language limitation (it does not support unsigned integers) only 31 bit is actually returned. Just like mt_rand() function, hw_rand() also discards the lowest bit and min/max range checking is implemented in the exact same manner so you can just call hw_rand() instead of mt_rand() or rand() in your PHP code.
Please note that rand(), mt_rand(), and hw_rand() are not cryptographically secure. Don't use those functions for cryptography — use openssl_random_pseudo_bytes() instead. Using rand(), mt_rand(), and hw_rand() for writing games and other non-critical stuff is ok.
Another very important thing to know is that PHP_INT_MAX on 64-bit Windows with 64-bit PHP build is still 231 - 1 instead of 263 - 1 which you might have expected. This seems to be the current limitation of the PHP language code base (it uses long datatype, which on the Windows platform is always 32 bits).