Paragon Initiative Enterprises Blog

The latest information from the team that develops cryptographically secure PHP software.

Common Uses for Cryptographically Secure Random Numbers

Our previous blog post discussed securely generating a random integer or string 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, 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.

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, but we request that you leave the link back to this blog post 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 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. 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):

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(), 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, 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, Paragon Initiative Enterprises is the team you want.

Our team of security consultants are experts in secure data encryption, short-term and long-term user authentication (i.e. "remember me" checkboxes), and web-based security vulnerabilities.

About the Author

P.I.E. Staff

Paragon Initiative Enterprises

Paragon Initiative Enterprises is a Florida-based company that provides software consulting, application development, code auditing, and security engineering services. We specialize in PHP Security and applied cryptography.


Need Technology Consultants?

Will tomorrow bring costly and embarrassing data breaches? Or will it bring growth, success, and peace of mind?

Our team of technology consultants have extensive knowledge and experience with application security and web/application development.

We specialize in cryptography and secure PHP development.

Let's Work Together Towards Success

Our Security Newsletters

Want the latest from Paragon Initiative Enterprises delivered straight to your inbox? We have two newsletters to choose from.

The first mails quarterly and often showcases our behind-the-scenes projects.

The other is unscheduled and gives you a direct feed into the findings of our open source security research initiatives.

Quarterly Newsletter   Security Announcements