Move PasswordUtil::secureRandomNumber() into CryptoUtil
authorTim Düsterhus <duesterhus@woltlab.com>
Wed, 16 Aug 2017 11:38:37 +0000 (13:38 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Wed, 16 Aug 2017 11:40:11 +0000 (13:40 +0200)
- Use random_int if available

Fixes #2379
Closes #2381

wcfsetup/install/files/lib/util/CryptoUtil.class.php
wcfsetup/install/files/lib/util/PasswordUtil.class.php

index 34b0389ddecc423980958685ab67387a1ce4624c..625825aabc43e1a0e2be49978246b22a8a9f1875 100644 (file)
@@ -101,8 +101,8 @@ final class CryptoUtil {
        }
        
        /**
-        * Compares a string of N random bytes.
-        * This function effectively is a polyfill for the PHP 7 `random_bytes`.
+        * Generates a string of N random bytes.
+        * This function effectively is a polyfill for the PHP 7 `random_bytes` function.
         * 
         * Requires either PHP 7 or 'openssl_random_pseudo_bytes' and throws a CryptoException
         * if no sufficiently random data could be obtained.
@@ -136,6 +136,41 @@ final class CryptoUtil {
                }
        }
        
+       /**
+        * Generates a random number.
+        * This function effectively is a polyfill for the PHP 7 `random_int` function.
+        * 
+        * Requires that self::randomBytes() does not throw.
+        * 
+        * @param       int     $min
+        * @param       int     $max
+        * @return      int
+        * @throws      CryptoException
+        */
+       public static function randomInt($min, $max) {
+               $range = $max - $min;
+               if ($range == 0) {
+                       // not random
+                       throw new CryptoException("Cannot generate a secure random number, min and max are the same");
+               }
+               
+               if (function_exists('random_int')) {
+                       return random_int($min, $max);
+               }
+               
+               $log = log($range, 2);
+               $bytes = (int) ($log / 8) + 1; // length in bytes
+               $bits = (int) $log + 1; // length in bits
+               $filter = (int) (1 << $bits) - 1; // set all lower bits to 1
+               do {
+                       $rnd = hexdec(bin2hex(self::randomBytes($bytes)));
+                       $rnd = $rnd & $filter; // discard irrelevant bits
+               }
+               while ($rnd > $range);
+               
+               return $min + $rnd;
+       }
+       
        /**
         * Forbid creation of CryptoUtil objects.
         */
index e0da9dc88fc7f6d1211d29dfe65f23a040b3daa5..82dd79e5246855c5f10e1d4d0db0b7307189f531 100644 (file)
@@ -2,6 +2,7 @@
 namespace wcf\util;
 use wcf\system\exception\SystemException;
 use wcf\system\Regex;
+use wcf\util\exception\CryptoException;
 
 /**
  * Provides functions to compute password hashes.
@@ -254,23 +255,14 @@ final class PasswordUtil {
                        // not random
                        throw new SystemException("Cannot generate a secure random number, min and max are the same");
                }
-               
-               // fallback to mt_rand() if OpenSSL is not available
-               if (!function_exists('openssl_random_pseudo_bytes')) {
-                       return mt_rand($min, $max);
+
+               try {
+                       return CryptoUtil::randomInt($min, $max);
                }
-               
-               $log = log($range, 2);
-               $bytes = (int) ($log / 8) + 1; // length in bytes
-               $bits = (int) $log + 1; // length in bits
-               $filter = (int) (1 << $bits) - 1; // set all lower bits to 1
-               do {
-                       $rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes, $s)));
-                       $rnd = $rnd & $filter; // discard irrelevant bits
+               catch (CryptoException $e) {
+                       // Backwards compatibility: This function never did throw.
+                       return mt_rand($min, $max);
                }
-               while ($rnd >= $range);
-               
-               return $min + $rnd;
        }
        
        /**