Our previous blog post discussed [securely generating a random integer or string in PHP](https://paragonie.com/blog/2015/07/how-safely-generate-random-strings-and-integers-in-php). Since it was published, we began working on a free and open source library to expose PHP 7's CSPRNG functions in PHP 5 projects, which we call [random_compat](https://github.com/paragonie/random_compat), with many great contributions from other PHP developers.
But also, we've discovered that the volume of bad information about random number generators offered to PHP developers is at least an order of magnitude higher than we anticipated. Since we've already done our homework on this subject, we'd like to provide some nifty helper functions that other PHP programmers are usually looking for.
## Cooking Hot PHP Functions with a CSPRNG
All of the functions on this page will require either PHP version 7, or version 5.6 with [random_compat](https://github.com/paragonie/random_compat).
If you're already using Composer, simply run this command to get started:
php composer.phar require paragonie/random_compat
Every snippet on this page is released under the [MIT License](http://opensource.org/licenses/MIT), but we *request* that you leave [the link back to this blog post](https://bit.ly/1ffqhGd) in the docblock comment so that future developers can see where it came from. As time goes on, we may add new functions to this page (or update existing ones).
Diceware Passphrase Generator
[Diceware](http://world.std.com/~reinhold/diceware.html) is a strategy for generating secure but easy-to-remember passphrases. Typically, you roll a six-sided die 5 times and use a lookup table to find your word. Then you repeat this process until you have your passphrase.
Implementing this in software requires deciding how you want to process [the Diceware wordlist](http://world.std.com/~reinhold/diceware.wordlist.asc). For example:
/**
* Read a PGP signed diceware wordlist file into an associative array
*
* @link https://paragonie.com/b/JvICXzh_jhLyt4y3
*
* @param string $filename Which file should we use?
*
* @return array
*/
function load_diceware_wordlist($filename = 'diceware.wordlist.asc')
{
if (!is_readabale($filename)) {
throw new Exception('File does not exist or is not readable: '.$filename);
}
$wordlist = fopen($filename, 'r');
$array = [];
while (!feof($fp)) {
$line = fread($fp);
// Now let's parse out the key => value
if (preg_match('/^([1-6]{5})\s*(.+)$/', $line, $matches)) {
$array[$matches[0]] = $matches[1];
}
}
fclose($fp);
return $array;
}
In a real application, you're far better off not reading this from a file on disk every page load.
Now that we have a wordlist source, creating a diceware passphrase generator is as easy as pie:
/**
* Generate a diceware passphrase
*
* @link https://paragonie.com/b/JvICXzh_jhLyt4y3
*
* @param int $numWords How many words do we want?
* @param string $joiner What do we want to join the words together with?
* @return string
*/
function diceware($numWords = 8, $joiner = ' ')
{
static $words = null;
if (empty($words)) {
// You probably want to just hard-code this array instead of fetching from disk
$words = load_diceware_wordlist();
}
$phrase = [];
for ($i = 0; $i < $numWords; ++$i) {
$key = '';
for ($j = 0; $j < 5; ++$j) {
$key .= (string) random_int(1, 6);
}
$phrase[] = $wordlist[$key];
}
return implode($joiner, $phrase);
}
Random String
Alternative to using Diceware for passphrases, you might just want to generate a random string. This is useful if you want to generate a temporary password that an attacker is unlikely to guess.
/**
* Generate a random string
*
* @link https://paragonie.com/b/JvICXzh_jhLyt4y3
*
* @param int $length - How long should our random string be?
* @param string $charset - A string of all possible characters to choose from
* @return string
*/
function random_str($length = 32, $charset = 'abcdefghijklmnopqrstuvwxyz')
{
// Type checks:
if (!is_numeric($length)) {
throw new InvalidArgumentException(
'random_str - Argument 1 - expected an integer'
);
}
if (!is_string($charset)) {
throw new InvalidArgumentException(
'random_str - Argument 2 - expected a string'
);
}
if ($length < 1) {
// Just return an empty string. Any value < 1 is meaningless.
return '';
}
// Remove duplicate characters from $charset
$split = str_split($charset);
$charset = implode('', array_unique($split));
// This is the maximum index for all of the characters in the string $charset
$charset_max = strlen($charset) - 1;
if ($charset_max < 1) {
// Avoid letting users do: random_str($int, 'a'); -> 'aaaaa...'
throw new LogicException(
'random_str - Argument 2 - expected a string that contains at least 2 distinct characters'
);
}
// Now that we have good data, this is the meat of our function:
$random_str = '';
for ($i = 0; $i < $length; ++$i) {
$r = random_int(0, $charset_max);
$random_str .= $charset[$r];
}
return $random_str;
}
To use it, simply specify how long you would like your string to be, and a string containing all of the possible characters. For example ([online demo](https://3v4l.org/hd3hL)):
var_dump(random_str(10));
// string(10) "chhojglciv"
var_dump(random_str(16, '0123456789'));
// string(16) "0343536507341807"
var_dump(random_str(8, '0123456789abcdef'));
// string(8) "0d884f99"
var_dump(random_str(24, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+.'));
// string(24) "RVzpSMe2M.zdzREesxoru4.f"
Shuffle an Ordered Array
This solution is similar to [how PHP internally implements `shuffle()`](https://github.com/php/php-src/blob/d8e4dcdfd617f1c1c942db63527d1d3838ede7c4/ext/standard/array.c#L1743-L1791), except we're using a CSPRNG. This is mostly useful if you, for example, need to implement an unpredictable shuffle for a virtual card game.
/**
* Shuffle an array using a CSPRNG
*
* @link https://paragonie.com/b/JvICXzh_jhLyt4y3
*
* @param &array $array reference to an array
*/
function secure_shuffle(&$array)
{
$size = count($array);
$keys = array_keys($array);
for ($i = $size - 1; $i > 0; --$i) {
$r = random_int(0, $i);
if ($r !== $i) {
$temp = $array[$keys[$r]];
$array[$keys[$r]] = $array[$keys[$i]];
$array[$keys[$i]] = $temp;
}
}
// Reset indices:
$array = array_values($array);
}
Universally Unique Identifier, Version 4 (Random)
If you need to implement [version 4 of the UUID specification](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29), the following function should do the trick:
/**
* Return a UUID (version 4) using random bytes
* Note that version 4 follows the format:
* xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
* where y is one of: [8, 9, A, B]
*
* We use (random_bytes(1) & 0x0F) | 0x40 to force
* the first character of hex value to always be 4
* in the appropriate position.
*
* For 4: http://3v4l.org/q2JN9
* For Y: http://3v4l.org/EsGSU
* For the whole shebang: http://3v4l.org/BkjBc
*
* @link https://paragonie.com/b/JvICXzh_jhLyt4y3
*
* @return string
*/
function uuidv4()
{
return implode('-', [
bin2hex(random_bytes(4)),
bin2hex(random_bytes(2)),
bin2hex(chr((ord(random_bytes(1)) & 0x0F) | 0x40)) . bin2hex(random_bytes(1)),
bin2hex(chr((ord(random_bytes(1)) & 0x3F) | 0x80)) . bin2hex(random_bytes(1)),
bin2hex(random_bytes(6))
]);
}
## Need a Custom Solution Built for your Business?
If you need a team that can develop a web, mobile, or client-server application with attention to [security above and beyond compliance](https://paragonie.com/service/appsec), Paragon Initiative Enterprises is the team you want.
Our team of security consultants are experts in [secure data encryption](https://paragonie.com/blog/2015/05/using-encryption-and-authentication-correctly), short-term and long-term user authentication (i.e. ["remember me" checkboxes](https://paragonie.com/blog/2015/04/secure-authentication-php-with-long-term-persistence#title.2)), and web-based security vulnerabilities.