diff --git a/RobThree/TwoFactorAuth/Providers/BaseHTTPQRCodeProvider.php b/RobThree/TwoFactorAuth/Providers/Qr/BaseHTTPQRCodeProvider.php similarity index 93% rename from RobThree/TwoFactorAuth/Providers/BaseHTTPQRCodeProvider.php rename to RobThree/TwoFactorAuth/Providers/Qr/BaseHTTPQRCodeProvider.php index f29265d..7cf2d82 100644 --- a/RobThree/TwoFactorAuth/Providers/BaseHTTPQRCodeProvider.php +++ b/RobThree/TwoFactorAuth/Providers/Qr/BaseHTTPQRCodeProvider.php @@ -1,6 +1,6 @@ verifyssl = $verifyssl; diff --git a/RobThree/TwoFactorAuth/Providers/IQRCodeProvider.php b/RobThree/TwoFactorAuth/Providers/Qr/IQRCodeProvider.php similarity index 72% rename from RobThree/TwoFactorAuth/Providers/IQRCodeProvider.php rename to RobThree/TwoFactorAuth/Providers/Qr/IQRCodeProvider.php index 6aeb856..8a0840a 100644 --- a/RobThree/TwoFactorAuth/Providers/IQRCodeProvider.php +++ b/RobThree/TwoFactorAuth/Providers/Qr/IQRCodeProvider.php @@ -1,6 +1,6 @@ verifyssl = $verifyssl; diff --git a/RobThree/TwoFactorAuth/Providers/QRicketProvider.php b/RobThree/TwoFactorAuth/Providers/Qr/QRicketProvider.php similarity index 96% rename from RobThree/TwoFactorAuth/Providers/QRicketProvider.php rename to RobThree/TwoFactorAuth/Providers/Qr/QRicketProvider.php index 2356235..bcd5e89 100644 --- a/RobThree/TwoFactorAuth/Providers/QRicketProvider.php +++ b/RobThree/TwoFactorAuth/Providers/Qr/QRicketProvider.php @@ -1,6 +1,6 @@ algorithm = $algorithm; + } + + public function getRandomBytes($bytecount) { + $result = array(); + for ($i = 0; $i < $bytecount; $i++) { + $hash = hash($this->algorithm, $hash.mt_rand(), true); + array_push($result, $hash[mt_rand(0, sizeof($hash))]); + } + return $result; + } +} diff --git a/RobThree/TwoFactorAuth/Providers/Rng/IRNGProvider.php b/RobThree/TwoFactorAuth/Providers/Rng/IRNGProvider.php new file mode 100644 index 0000000..d401827 --- /dev/null +++ b/RobThree/TwoFactorAuth/Providers/Rng/IRNGProvider.php @@ -0,0 +1,8 @@ +source = $source; + } + + public function getRandomBytes($bytecount) { + $result = mcrypt_create_iv($bytecount, $this->source); + if ($result === false) + throw new RNGException('mcrypt_create_iv returned an invalid value'); + return $result; + } +} \ No newline at end of file diff --git a/RobThree/TwoFactorAuth/Providers/Rng/OpenSSLRNGProvider.php b/RobThree/TwoFactorAuth/Providers/Rng/OpenSSLRNGProvider.php new file mode 100644 index 0000000..edab2cd --- /dev/null +++ b/RobThree/TwoFactorAuth/Providers/Rng/OpenSSLRNGProvider.php @@ -0,0 +1,21 @@ +requirestrong = $requirestrong; + } + + public function getRandomBytes($bytecount) { + $result = openssl_random_pseudo_bytes($bytecount, $crypto_strong); + if ($this->requirestrong && ($crypto_strong === false)) + throw new RNGException('openssl_random_pseudo_bytes returned non-cryptographically strong value'); + if ($result === false) + throw new RNGException('openssl_random_pseudo_bytes returned an invalid value'); + return $result; + } +} \ No newline at end of file diff --git a/RobThree/TwoFactorAuth/Providers/Rng/RNGException.php b/RobThree/TwoFactorAuth/Providers/Rng/RNGException.php new file mode 100644 index 0000000..882d7f7 --- /dev/null +++ b/RobThree/TwoFactorAuth/Providers/Rng/RNGException.php @@ -0,0 +1,11 @@ +issuer = $issuer; @@ -32,14 +33,31 @@ class TwoFactorAuth throw new Exception('Unsupported algorithm: ' . $algorithm); $this->algorithm = $algorithm; + // Set default QR Code provider if none was specified if ($qrcodeprovider==null) - $qrcodeprovider = new Providers\GoogleQRCodeProvider(); + $qrcodeprovider = new Providers\Qr\GoogleQRCodeProvider(); - if (!($qrcodeprovider instanceof Providers\IQRCodeProvider)) + if (!($qrcodeprovider instanceof Providers\Qr\IQRCodeProvider)) throw new Exception('QRCodeProvider must implement IQRCodeProvider'); $this->qrcodeprovider = $qrcodeprovider; + // Try to find best available RNG provider if none was specified + if ($rngprovider==null) { + if (function_exists('mcrypt_create_iv')) { + $rngprovider = new Providers\Rng\MCryptRNGProvider(); + } elseif (function_exists('openssl_random_pseudo_bytes')) { + $rngprovider = new Providers\Rng\OpenSSLRNGProvider(); + } else { + $rngprovider = new Providers\Rng\HashRNGProvider(); + } + } + + if (!($rngprovider instanceof Providers\Rng\IRNGProvider)) + throw new Exception('RNGProvider must implement IRNGProvider'); + + $this->rngprovider = $rngprovider; + self::$_base32 = str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567='); self::$_base32lookup = array_flip(self::$_base32); } @@ -51,7 +69,7 @@ class TwoFactorAuth { $secret = ''; $bytes = ceil($bits / 5); //We use 5 bits of each byte (since we have a 32-character 'alphabet' / BASE32) - $rnd = openssl_random_pseudo_bytes($bytes); + $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 return $secret; diff --git a/TwoFactorAuth.phpproj b/TwoFactorAuth.phpproj index ca8028e..f9f9b3d 100644 --- a/TwoFactorAuth.phpproj +++ b/TwoFactorAuth.phpproj @@ -24,11 +24,17 @@ - - - - - + + + + + + + + + + + @@ -37,6 +43,8 @@ + +