note 114898 deleted from function.mt-rand by cmb
| From: | cmb@php.net | Date: | Sun, 10 Nov 2019 12:44:13 +0000 |
| Subject: | note 114898 deleted from function.mt-rand by cmb | ||
| References: | 1 | Groups: | php.notes |
| Request: | Send a blank email to php-notes+get-212996@lists.php.net to get a copy of this message | ||
Note Submitter: github.com/elcodedocle
----
Ever wanted a crypto safe mt_rand()? Not useful for many things I guess (maybe not useful at all),
but I recently wanted to build a class able to pick random words from a dictionary in a safe way, so
they could be used as passwords. I probably overdid it... The result is my attempt on a
"cryptographically secure" mt_rand implementation including a wrapper for selecting and
using the best random bytes generator from available extensions and /dev/urandom (about 100 times
slower than mt_rand on a single core, by the way, so it's clearly open for improvement if you
want to fork it on github.com/elcodedocle/cryptosecureprng, where you can find the docs to it too).
Here is the minimized version (note too long):
<?php
/**
* Crypto Secure PRNG
* @copyright 2014 elcodedocle
*/
class CryptoSecurePRNG {
private $intByteCount = 4;
private $defaultMax = 2147483647;
private $mcrypt;
private $openssl;
private $urandomFileHandler = false;
public function getRandomBytesString($byteCount){
$chars = '';
if ($this->mcrypt) {
$chars = mcrypt_create_iv($byteCount, MCRYPT_DEV_URANDOM);
}
if (!$chars && $this->openssl) {
$chars = openssl_random_pseudo_bytes($byteCount);
}
if (!$chars && is_resource($this->urandomFileHandler)) {
while (($len = strlen($chars))< $byteCount) {
$chars .= fread($this->urandomFileHandler, $byteCount - $len);
}
}
if ($len = strlen($chars) < $byteCount) {
for ($i=0;$i<$len;$i++) { $chars[$i] = $chars[$i] ^ chr(mt_rand(0, 255)); }
for ($i = $len; $i < $byteCount; $i++) {
$chars .= chr(mt_rand(0, 255));
}
}
return $chars;
}
public function rand($min = 0, $max = null){
if ($max === null) { $max = $this->defaultMax; }
if (!is_int($min)||!is_int($max)) { throw new Exception('$min and $max must be
integers'); }
if ($min>$max) { throw new Exception('$min must be <= $max'); }
$chars = $this->getRandomBytesString($this->intByteCount);
$n = 0;
for ($i=0;$i<strlen($chars);$i++)
{$n|=(ord($chars[$i])<<(8*(strlen($chars)-$i-1)));}
return (abs($n)%($max-$min+1))+$min;
}
public function __construct(){
$this->mcrypt = function_exists('mcrypt_create_iv');
$this->openssl = function_exists('openssl_random_pseudo_bytes');
if (is_readable('/dev/urandom')){ $this->urandomFileHandler =
fopen('/dev/urandom', 'r'); }
}
public function __destruct(){
if (is_resource($this->urandomFileHandler )) fclose($this->urandomFileHandler);
}
}
$randGen = new CryptoSecurePRNG();
echo $randGen->rand(); //outputs rand int between 0 and 2^32