Compare commits

...

10 Commits

Author SHA1 Message Date
Rob Janssen cdb98dc598 Update composer.json 2016-06-20 23:15:37 +02:00
RobThree 3484cdd992 * Removed CURLOPT_FOLLOWLOCATION and CURLOPT_MAXREDIRS (see #5) 2016-06-20 23:14:36 +02:00
Rob Janssen 7d7b4ddcba Merge pull request #2 from andrej-griniuk/patch-1
Added the link to the integration with CakePHP
2016-02-16 10:37:40 +01:00
Andrej Griniuk c52e9e3728 Added the link to the integration with CakePHP 2016-02-15 17:59:37 +11:00
Rob Janssen a62c8c6332 Update README.md 2015-12-04 13:35:36 +01:00
Rob Janssen dc77020a5a Update README.md 2015-12-04 13:34:30 +01:00
Rob Janssen 9043dd15f8 Update README.md 2015-12-02 16:50:37 +01:00
RobThree f546c73537 * Added CSRNGProvider to requires in unittestfile. 2015-12-02 16:40:18 +01:00
RobThree c94fcd83d8 * 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
2015-12-02 16:38:20 +01:00
Rob Janssen 856167c950 Update README.md 2015-11-21 01:09:38 +01:00
9 changed files with 91 additions and 14 deletions
+7 -3
View File
@@ -12,7 +12,7 @@ PHP library for [two-factor (or multi-factor) authentication](http://en.wikipedi
* Tested on PHP 5.3, 5.4, 5.5 and 5.6, 7 and HHVM
* [cURL](http://php.net/manual/en/book.curl.php) when using the provided `GoogleQRCodeProvider` (default), `QRServerProvider` or `QRicketProvider` but you can also provide your own QR-code provider.
* [MCrypt](http://php.net/manual/en/book.mcrypt.php), [OpenSSL](http://php.net/manual/en/book.openssl.php) or [Hash](http://php.net/manual/en/book.hash.php) depending on which built-in RNG you use (TwoFactorAuth will try to 'autodetect' and use the best available); however: feel free to provide your own (CS)RNG.
* [random_bytes()](http://php.net/manual/en/function.random-bytes.php), [MCrypt](http://php.net/manual/en/book.mcrypt.php), [OpenSSL](http://php.net/manual/en/book.openssl.php) or [Hash](http://php.net/manual/en/book.hash.php) depending on which built-in RNG you use (TwoFactorAuth will try to 'autodetect' and use the best available); however: feel free to provide your own (CS)RNG.
## Installation
@@ -81,7 +81,7 @@ When outputting a QR-code you can choose a `$label` for the user (which, when en
### Step 2: Verify secret shared key
When the code is added to the app, the app will be ready to start generating codes which 'expire' each '`$period`' number of seconds. To make sure the secret was entered, or scanned, correctly you need to verify this by having the user enter a generated code. To check if the generated code is valid you call the `verifyCode()` method:
When the shared secret is added to the app, the app will be ready to start generating codes which 'expire' each '`$period`' number of seconds. To make sure the secret was entered, or scanned, correctly you need to verify this by having the user enter a generated code. To check if the generated code is valid you call the `verifyCode()` method:
````php
// Verify code
@@ -166,10 +166,14 @@ Voilà. Couldn't make it any simpler.
### RNG providers
This library also comes with three 'built-in' RNG providers ([Random Number Generator](https://en.wikipedia.org/wiki/Random_number_generation)). The RNG provider generates a number of random bytes and returns these bytes as a string. These values are then used to create the secret. By default (no RNG provider specified) TwoFactorAuth will try to determine the best available RNG provider to use. It will, by default, try to use the [`MCryptRNGProvider`](lib/Providers/Rng/MCryptRNGProvider.php), if this is not available/supported for any reason it will try to use the [`OpenSSLRNGProvider`](lib/Providers/Rng/OpenSSLRNGProvider.php) and if that is also not available/supported it will try to use the final RNG provider: [`HashRNGProvider`](lib/Providers/Rng/HashRNGProvider.php). Each of these providers use their own method of generating a random sequence of bytes. The first two (`OpenSSLRNGProvider` and `MCryptRNGProvider`) return a [cryptographically secure](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator) sequence of random bytes whereas the `HashRNGProvider` returns a **non-cryptographically secure** sequence.
This library also comes with three 'built-in' RNG providers ([Random Number Generator](https://en.wikipedia.org/wiki/Random_number_generation)). The RNG provider generates a number of random bytes and returns these bytes as a string. These values are then used to create the secret. By default (no RNG provider specified) TwoFactorAuth will try to determine the best available RNG provider to use. It will, by default, try to use the [`CSRNGProvider`](lib/Providers/Rng/CSRNGProvider.php) for PHP7+ or the [`MCryptRNGProvider`](lib/Providers/Rng/MCryptRNGProvider.php); if this is not available/supported for any reason it will try to use the [`OpenSSLRNGProvider`](lib/Providers/Rng/OpenSSLRNGProvider.php) and if that is also not available/supported it will try to use the final RNG provider: [`HashRNGProvider`](lib/Providers/Rng/HashRNGProvider.php). Each of these providers use their own method of generating a random sequence of bytes. The first three (`CSRNGProvider`, `OpenSSLRNGProvider` and `MCryptRNGProvider`) return a [cryptographically secure](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator) sequence of random bytes whereas the `HashRNGProvider` returns a **non-cryptographically secure** sequence.
You can easily implement your own `RNGProvider` by simply implementing the `IRNGProvider` interface. Each of the 'built-in' RNG providers have some constructor parameters that allow you to 'tweak' some of the settings to use when creating the random bytes such as which source to use (`MCryptRNGProvider`) or which hashing algorithm (`HashRNGProvider`). I encourage you to have a look at some of the ['built-in' RNG providers](lib/Providers/Rng) for details and the [`IRNGProvider` interface](lib/Providers/Rng/IRNGProvider.php).
## Integrations
- [CakePHP 3](https://github.com/andrej-griniuk/cakephp-two-factor-auth)
## License
Licensed under MIT license. See [LICENSE](https://raw.githubusercontent.com/RobThree/TwoFactorAuth/master/LICENSE) for details.
+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.5",
"type": "library",
"keywords": [ "Authentication", "Two Factor Authentication", "Multi Factor Authentication", "TFA", "MFA", "PHP", "Authenticator", "Authy" ],
"homepage": "https://github.com/RobThree/TwoFactorAuth",
@@ -12,8 +12,6 @@ abstract class BaseHTTPQRCodeProvider implements IQRCodeProvider
curl_setopt_array($curlhandle, array(
CURLOPT_URL => $url,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_DNS_CACHE_TIMEOUT => 10,
+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();
+63 -7
View File
@@ -8,6 +8,7 @@ require_once 'lib/Providers/Qr/GoogleQRCodeProvider.php';
require_once 'lib/Providers/Rng/IRNGProvider.php';
require_once 'lib/Providers/Rng/RNGException.php';
require_once 'lib/Providers/Rng/CSRNGProvider.php';
require_once 'lib/Providers/Rng/MCryptRNGProvider.php';
require_once 'lib/Providers/Rng/OpenSSLRNGProvider.php';
require_once 'lib/Providers/Rng/HashRNGProvider.php';
@@ -24,7 +25,7 @@ class TwoFactorAuthTest extends PHPUnit_Framework_TestCase
*/
public function testConstructorThrowsOnInvalidDigits() {
$tfa = new TwoFactorAuth('Test', 0);
new TwoFactorAuth('Test', 0);
}
/**
@@ -32,7 +33,7 @@ class TwoFactorAuthTest extends PHPUnit_Framework_TestCase
*/
public function testConstructorThrowsOnInvalidPeriod() {
$tfa = new TwoFactorAuth('Test', 6, 0);
new TwoFactorAuth('Test', 6, 0);
}
/**
@@ -40,7 +41,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 +49,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 +57,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 +148,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 +156,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 +243,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(