mirror of
https://github.com/RobThree/TwoFactorAuth.git
synced 2026-08-20 20:52:54 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cdb98dc598 | |||
| 3484cdd992 | |||
| 7d7b4ddcba | |||
| c52e9e3728 | |||
| a62c8c6332 | |||
| dc77020a5a | |||
| 9043dd15f8 | |||
| f546c73537 | |||
| c94fcd83d8 | |||
| 856167c950 |
@@ -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.
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user