From 2a358417318c8b17eb0f7035b992b4b796879763 Mon Sep 17 00:00:00 2001
From: Jared Hancock <jared@osticket.com>
Date: Tue, 13 Jan 2015 10:58:22 -0600
Subject: [PATCH] Fix very predictable random data on some platforms

Misc::randCode does not generate significantly random data for Windows
platforms with a local database. This stems from the random seed using the
milliseconds from the current time of day and the database connection time,
in microseconds. Because Windows has especially poor sub-second time
resolution via the microtime() function, the seed does not have many
variations.

This patch addresses the issue by using the included Crypto::random()
function as a source of random data rather than the mt_rand() function, as
it uses native cryptographic random data generators if possible to generate
the data, and uses microtime() as a fallback if no other source of random
data is available on the platform.
---
 include/class.misc.php | 33 +++++++++++++++++++++++++--------
 1 file changed, 25 insertions(+), 8 deletions(-)

diff --git a/include/class.misc.php b/include/class.misc.php
index 84871c539..cf1d0b32d 100644
--- a/include/class.misc.php
+++ b/include/class.misc.php
@@ -16,14 +16,31 @@
 
 class Misc {
 
-	function randCode($count=8, $chars=false) {
-        $chars = $chars ? $chars
-            : 'abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.';
-        $data = '';
-        $m = strlen($chars) - 1;
-        for ($i=0; $i < $count; $i++)
-            $data .= $chars[mt_rand(0,$m)];
-        return $data;
+	function randCode($len=8, $chars=false) {
+        $chars = $chars ?: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_=';
+
+        // Determine the number of bits we need
+        $char_count = strlen($chars);
+        $bits_per_char = ceil(log($char_count, 2));
+        $bytes = ceil(4 * $len / floor(32 / $bits_per_char));
+        // Pad to 4 byte boundary
+        $bytes += (4 - ($bytes % 4)) % 4;
+
+        // Fetch some random data blocks
+        $data = Crypto::random($bytes);
+
+        $mask = (1 << $bits_per_char) - 1;
+        $loops = (int) (32 / $bits_per_char);
+        $output = '';
+        $ints = unpack('V*', $data);
+        array_shift($ints);
+        foreach ($ints as $int) {
+            for ($i = $loops; $i > 0; $i--) {
+                $output .= $chars[($int & $mask) % $char_count];
+                $int >>= $bits_per_char;
+            }
+        }
+        return substr($output, 0, $len);
 	}
 
     function __rand_seed($value=0) {
-- 
GitLab