* Added new (PHP7+) CSRNGProvider (uses random_bytes)

* Added a bunch basic of unittests for the RNG's
* QRicketProvider and QRServerProvider now throw on invalid MIME-types
* TwoFactorAuth now first tries to use CSRNGProvider before any of the other RNG providers
This commit is contained in:
RobThree
2015-12-02 16:38:20 +01:00
parent 856167c950
commit c94fcd83d8
7 changed files with 83 additions and 9 deletions
+1
View File
@@ -30,6 +30,7 @@
<Compile Include="lib\Providers\Qr\QRException.php" />
<Compile Include="lib\Providers\Qr\QRicketProvider.php" />
<Compile Include="lib\Providers\Qr\QRServerProvider.php" />
<Compile Include="lib\Providers\Rng\CSRNGProvider.php" />
<Compile Include="lib\Providers\Rng\IRNGProvider.php" />
<Compile Include="lib\Providers\Rng\MCryptRNGProvider.php" />
<Compile Include="lib\Providers\Rng\OpenSSLRNGProvider.php" />
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "robthree/twofactorauth",
"description": "Two Factor Authentication",
"version": "1.3",
"version": "1.4",
"type": "library",
"keywords": [ "Authentication", "Two Factor Authentication", "Multi Factor Authentication", "TFA", "MFA", "PHP", "Authenticator", "Authy" ],
"homepage": "https://github.com/RobThree/TwoFactorAuth",
+1
View File
@@ -43,6 +43,7 @@ class QRServerProvider extends BaseHTTPQRCodeProvider
case 'eps':
return 'application/postscript';
}
throw new QRException(sprintf('Unknown MIME-type: %s', $this->format));
}
public function getQRCodeImage($qrtext, $size)
+1
View File
@@ -33,6 +33,7 @@ class QRicketProvider extends BaseHTTPQRCodeProvider
case 'j':
return 'image/jpeg';
}
throw new QRException(sprintf('Unknown MIME-type: %s', $this->format));
}
public function getQRCodeImage($qrtext, $size)
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace RobThree\Auth\Providers\Rng;
class CSRNGProvider implements IRNGProvider
{
public function getRandomBytes($bytecount) {
return random_bytes($bytecount); // PHP7+
}
public function isCryptographicallySecure() {
return true;
}
}
+3 -1
View File
@@ -45,7 +45,9 @@ class TwoFactorAuth
// Try to find best available RNG provider if none was specified
if ($rngprovider==null) {
if (function_exists('mcrypt_create_iv')) {
if (function_exists('random_bytes')) {
$rngprovider = new Providers\Rng\CSRNGProvider();
} elseif (function_exists('mcrypt_create_iv')) {
$rngprovider = new Providers\Rng\MCryptRNGProvider();
} elseif (function_exists('openssl_random_pseudo_bytes')) {
$rngprovider = new Providers\Rng\OpenSSLRNGProvider();
+62 -7
View File
@@ -24,7 +24,7 @@ class TwoFactorAuthTest extends PHPUnit_Framework_TestCase
*/
public function testConstructorThrowsOnInvalidDigits() {
$tfa = new TwoFactorAuth('Test', 0);
new TwoFactorAuth('Test', 0);
}
/**
@@ -32,7 +32,7 @@ class TwoFactorAuthTest extends PHPUnit_Framework_TestCase
*/
public function testConstructorThrowsOnInvalidPeriod() {
$tfa = new TwoFactorAuth('Test', 6, 0);
new TwoFactorAuth('Test', 6, 0);
}
/**
@@ -40,7 +40,7 @@ class TwoFactorAuthTest extends PHPUnit_Framework_TestCase
*/
public function testConstructorThrowsOnInvalidAlgorithm() {
$tfa = new TwoFactorAuth('Test', 6, 30, 'xxx');
new TwoFactorAuth('Test', 6, 30, 'xxx');
}
/**
@@ -48,7 +48,7 @@ class TwoFactorAuthTest extends PHPUnit_Framework_TestCase
*/
public function testConstructorThrowsOnQrProviderNotImplementingInterface() {
$tfa = new TwoFactorAuth('Test', 6, 30, 'sha1', new stdClass());
new TwoFactorAuth('Test', 6, 30, 'sha1', new stdClass());
}
/**
@@ -56,7 +56,7 @@ class TwoFactorAuthTest extends PHPUnit_Framework_TestCase
*/
public function testConstructorThrowsOnRngProviderNotImplementingInterface() {
$tfa = new TwoFactorAuth('Test', 6, 30, 'sha1', null, new stdClass());
new TwoFactorAuth('Test', 6, 30, 'sha1', null, new stdClass());
}
public function testGetCodeReturnsCorrectResults() {
@@ -147,7 +147,7 @@ class TwoFactorAuthTest extends PHPUnit_Framework_TestCase
*/
public function testGetCodeThrowsOnInvalidBase32String1() {
$tfa = new TwoFactorAuth('Test');
$result = $tfa->getCode('FOO1BAR8BAZ9'); //1, 8 & 9 are invalid chars
$tfa->getCode('FOO1BAR8BAZ9'); //1, 8 & 9 are invalid chars
}
/**
@@ -155,7 +155,7 @@ class TwoFactorAuthTest extends PHPUnit_Framework_TestCase
*/
public function testGetCodeThrowsOnInvalidBase32String2() {
$tfa = new TwoFactorAuth('Test');
$result = $tfa->getCode('mzxw6==='); //Lowercase
$tfa->getCode('mzxw6==='); //Lowercase
}
public function testKnownBase32DecodeTestVectors() {
@@ -242,7 +242,62 @@ class TwoFactorAuthTest extends PHPUnit_Framework_TestCase
$this->assertEquals('47863826', $tfa->getCode($secret, 20000000000));
}
/**
* @requires function random_bytes
*/
public function testCSRNGProvidersReturnExpectedNumberOfBytes() {
$rng = new \RobThree\Auth\Providers\Rng\CSRNGProvider();
foreach ($this->getRngTestLengths() as $l)
$this->assertEquals($l, strlen($rng->getRandomBytes($l)));
$this->assertEquals(true, $rng->isCryptographicallySecure());
}
/**
* @requires function hash_algos
* @requires function hash
*/
public function testHashRNGProvidersReturnExpectedNumberOfBytes() {
$rng = new \RobThree\Auth\Providers\Rng\HashRNGProvider();
foreach ($this->getRngTestLengths() as $l)
$this->assertEquals($l, strlen($rng->getRandomBytes($l)));
$this->assertEquals(false, $rng->isCryptographicallySecure());
}
/**
* @requires function mcrypt_create_iv
*/
public function testMCryptRNGProvidersReturnExpectedNumberOfBytes() {
$rng = new \RobThree\Auth\Providers\Rng\MCryptRNGProvider();
foreach ($this->getRngTestLengths() as $l)
$this->assertEquals($l, strlen($rng->getRandomBytes($l)));
$this->assertEquals(true, $rng->isCryptographicallySecure());
}
/**
* @requires function openssl_random_pseudo_bytes
*/
public function testStrongOpenSSLRNGProvidersReturnExpectedNumberOfBytes() {
$rng = new \RobThree\Auth\Providers\Rng\OpenSSLRNGProvider(true);
foreach ($this->getRngTestLengths() as $l)
$this->assertEquals($l, strlen($rng->getRandomBytes($l)));
$this->assertEquals(true, $rng->isCryptographicallySecure());
}
/**
* @requires function openssl_random_pseudo_bytes
*/
public function testNonStrongOpenSSLRNGProvidersReturnExpectedNumberOfBytes() {
$rng = new \RobThree\Auth\Providers\Rng\OpenSSLRNGProvider(false);
foreach ($this->getRngTestLengths() as $l)
$this->assertEquals($l, strlen($rng->getRandomBytes($l)));
$this->assertEquals(false, $rng->isCryptographicallySecure());
}
private function getRngTestLengths() {
return array(1, 16, 32, 256);
}
private function DecodeDataUri($datauri) {
if (preg_match('/data:(?P<mimetype>[\w\.\-\/]+);(?P<encoding>\w+),(?P<data>.*)/', $datauri, $m) === 1) {
return array(