diff --git a/RobThree/TwoFactorAuth/Providers/Rng/HashRNGProvider.php b/RobThree/TwoFactorAuth/Providers/Rng/HashRNGProvider.php index 8a7b06d..645c7dc 100644 --- a/RobThree/TwoFactorAuth/Providers/Rng/HashRNGProvider.php +++ b/RobThree/TwoFactorAuth/Providers/Rng/HashRNGProvider.php @@ -13,11 +13,16 @@ class HashRNGProvider implements IRNGProvider } public function getRandomBytes($bytecount) { - $result = array(); + $result = ''; + $hash = mt_rand(); for ($i = 0; $i < $bytecount; $i++) { $hash = hash($this->algorithm, $hash.mt_rand(), true); - array_push($result, $hash[mt_rand(0, sizeof($hash))]); + $result .= $hash[mt_rand(0, sizeof($hash))]; } return $result; } + + public function isCryptographicallySecure() { + return false; + } } diff --git a/RobThree/TwoFactorAuth/Providers/Rng/IRNGProvider.php b/RobThree/TwoFactorAuth/Providers/Rng/IRNGProvider.php index d401827..44bb9d5 100644 --- a/RobThree/TwoFactorAuth/Providers/Rng/IRNGProvider.php +++ b/RobThree/TwoFactorAuth/Providers/Rng/IRNGProvider.php @@ -5,4 +5,5 @@ namespace RobThree\TwoFactorAuth\Providers\Rng; interface IRNGProvider { public function getRandomBytes($bytecount); + public function isCryptographicallySecure(); } \ No newline at end of file diff --git a/RobThree/TwoFactorAuth/Providers/Rng/MCryptRNGProvider.php b/RobThree/TwoFactorAuth/Providers/Rng/MCryptRNGProvider.php index 8618ca3..b97be1a 100644 --- a/RobThree/TwoFactorAuth/Providers/Rng/MCryptRNGProvider.php +++ b/RobThree/TwoFactorAuth/Providers/Rng/MCryptRNGProvider.php @@ -16,4 +16,8 @@ class MCryptRNGProvider implements IRNGProvider throw new RNGException('mcrypt_create_iv returned an invalid value'); return $result; } + + public function isCryptographicallySecure() { + return true; + } } \ No newline at end of file diff --git a/RobThree/TwoFactorAuth/Providers/Rng/OpenSSLRNGProvider.php b/RobThree/TwoFactorAuth/Providers/Rng/OpenSSLRNGProvider.php index edab2cd..2105136 100644 --- a/RobThree/TwoFactorAuth/Providers/Rng/OpenSSLRNGProvider.php +++ b/RobThree/TwoFactorAuth/Providers/Rng/OpenSSLRNGProvider.php @@ -18,4 +18,8 @@ class OpenSSLRNGProvider implements IRNGProvider throw new RNGException('openssl_random_pseudo_bytes returned an invalid value'); return $result; } + + public function isCryptographicallySecure() { + return $this->requirestrong; + } } \ No newline at end of file diff --git a/RobThree/TwoFactorAuth/TwoFactorAuth.php b/RobThree/TwoFactorAuth/TwoFactorAuth.php index 08fa1be..4273bbb 100644 --- a/RobThree/TwoFactorAuth/TwoFactorAuth.php +++ b/RobThree/TwoFactorAuth/TwoFactorAuth.php @@ -21,16 +21,16 @@ class TwoFactorAuth $this->issuer = $issuer; if (!is_int($digits) || $digits <= 0) - throw new Exception('Digits must be int > 0'); + throw new TwoFactorAuthException('Digits must be int > 0'); $this->digits = $digits; if (!is_int($period) || $period <= 0) - throw new Exception('Period must be int > 0'); + throw new TwoFactorAuthException('Period must be int > 0'); $this->period = $period; $algorithm = strtolower(trim($algorithm)); if (!in_array($algorithm, self::$_supportedalgos)) - throw new Exception('Unsupported algorithm: ' . $algorithm); + throw new TwoFactorAuthException('Unsupported algorithm: ' . $algorithm); $this->algorithm = $algorithm; // Set default QR Code provider if none was specified @@ -38,7 +38,7 @@ class TwoFactorAuth $qrcodeprovider = new Providers\Qr\GoogleQRCodeProvider(); if (!($qrcodeprovider instanceof Providers\Qr\IQRCodeProvider)) - throw new Exception('QRCodeProvider must implement IQRCodeProvider'); + throw new TwoFactorAuthException('QRCodeProvider must implement IQRCodeProvider'); $this->qrcodeprovider = $qrcodeprovider; @@ -54,7 +54,7 @@ class TwoFactorAuth } if (!($rngprovider instanceof Providers\Rng\IRNGProvider)) - throw new Exception('RNGProvider must implement IRNGProvider'); + throw new TwoFactorAuthException('RNGProvider must implement IRNGProvider'); $this->rngprovider = $rngprovider; @@ -65,10 +65,12 @@ class TwoFactorAuth /** * Create a new secret */ - public function createSecret($bits = 80) + public function createSecret($bits = 80, $requirecryptosecure = true) { $secret = ''; $bytes = ceil($bits / 5); //We use 5 bits of each byte (since we have a 32-character 'alphabet' / BASE32) + if ($requirecryptosecure && !$this->rngprovider->isCryptographicallySecure()) + throw new TwoFactorAuthException('RNG provider is not cryptographically secure'); $rnd = $this->rngprovider->getRandomBytes($bytes); for ($i = 0; $i < $bytes; $i++) $secret .= self::$_base32[ord($rnd[$i]) & 31]; //Mask out left 3 bits for 0-31 values @@ -112,7 +114,7 @@ class TwoFactorAuth public function getQRCodeImageAsDataUri($label, $secret, $size = 200) { if (!is_int($size) || $size < 0) - throw new Exception('Size must be int > 0'); + throw new TwoFactorAuthException('Size must be int > 0'); return 'data:' . $this->qrcodeprovider->getMimeType() diff --git a/RobThree/TwoFactorAuth/TwoFactorAuthException.php b/RobThree/TwoFactorAuth/TwoFactorAuthException.php new file mode 100644 index 0000000..9de9d3a --- /dev/null +++ b/RobThree/TwoFactorAuth/TwoFactorAuthException.php @@ -0,0 +1,11 @@ + +