mirror of
https://github.com/RobThree/TwoFactorAuth.git
synced 2026-08-25 07:26:35 +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
|
* 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.
|
* [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
|
## 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
|
### 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
|
````php
|
||||||
// Verify code
|
// Verify code
|
||||||
@@ -166,10 +166,14 @@ Voilà. Couldn't make it any simpler.
|
|||||||
|
|
||||||
### RNG providers
|
### 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).
|
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
|
## License
|
||||||
|
|
||||||
Licensed under MIT license. See [LICENSE](https://raw.githubusercontent.com/RobThree/TwoFactorAuth/master/LICENSE) for details.
|
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\QRException.php" />
|
||||||
<Compile Include="lib\Providers\Qr\QRicketProvider.php" />
|
<Compile Include="lib\Providers\Qr\QRicketProvider.php" />
|
||||||
<Compile Include="lib\Providers\Qr\QRServerProvider.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\IRNGProvider.php" />
|
||||||
<Compile Include="lib\Providers\Rng\MCryptRNGProvider.php" />
|
<Compile Include="lib\Providers\Rng\MCryptRNGProvider.php" />
|
||||||
<Compile Include="lib\Providers\Rng\OpenSSLRNGProvider.php" />
|
<Compile Include="lib\Providers\Rng\OpenSSLRNGProvider.php" />
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "robthree/twofactorauth",
|
"name": "robthree/twofactorauth",
|
||||||
"description": "Two Factor Authentication",
|
"description": "Two Factor Authentication",
|
||||||
"version": "1.3",
|
"version": "1.5",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"keywords": [ "Authentication", "Two Factor Authentication", "Multi Factor Authentication", "TFA", "MFA", "PHP", "Authenticator", "Authy" ],
|
"keywords": [ "Authentication", "Two Factor Authentication", "Multi Factor Authentication", "TFA", "MFA", "PHP", "Authenticator", "Authy" ],
|
||||||
"homepage": "https://github.com/RobThree/TwoFactorAuth",
|
"homepage": "https://github.com/RobThree/TwoFactorAuth",
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ abstract class BaseHTTPQRCodeProvider implements IQRCodeProvider
|
|||||||
|
|
||||||
curl_setopt_array($curlhandle, array(
|
curl_setopt_array($curlhandle, array(
|
||||||
CURLOPT_URL => $url,
|
CURLOPT_URL => $url,
|
||||||
CURLOPT_FOLLOWLOCATION => true,
|
|
||||||
CURLOPT_MAXREDIRS => 3,
|
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
CURLOPT_CONNECTTIMEOUT => 10,
|
CURLOPT_CONNECTTIMEOUT => 10,
|
||||||
CURLOPT_DNS_CACHE_TIMEOUT => 10,
|
CURLOPT_DNS_CACHE_TIMEOUT => 10,
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ class QRServerProvider extends BaseHTTPQRCodeProvider
|
|||||||
case 'eps':
|
case 'eps':
|
||||||
return 'application/postscript';
|
return 'application/postscript';
|
||||||
}
|
}
|
||||||
|
throw new QRException(sprintf('Unknown MIME-type: %s', $this->format));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getQRCodeImage($qrtext, $size)
|
public function getQRCodeImage($qrtext, $size)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class QRicketProvider extends BaseHTTPQRCodeProvider
|
|||||||
case 'j':
|
case 'j':
|
||||||
return 'image/jpeg';
|
return 'image/jpeg';
|
||||||
}
|
}
|
||||||
|
throw new QRException(sprintf('Unknown MIME-type: %s', $this->format));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getQRCodeImage($qrtext, $size)
|
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
|
// Try to find best available RNG provider if none was specified
|
||||||
if ($rngprovider==null) {
|
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();
|
$rngprovider = new Providers\Rng\MCryptRNGProvider();
|
||||||
} elseif (function_exists('openssl_random_pseudo_bytes')) {
|
} elseif (function_exists('openssl_random_pseudo_bytes')) {
|
||||||
$rngprovider = new Providers\Rng\OpenSSLRNGProvider();
|
$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/IRNGProvider.php';
|
||||||
require_once 'lib/Providers/Rng/RNGException.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/MCryptRNGProvider.php';
|
||||||
require_once 'lib/Providers/Rng/OpenSSLRNGProvider.php';
|
require_once 'lib/Providers/Rng/OpenSSLRNGProvider.php';
|
||||||
require_once 'lib/Providers/Rng/HashRNGProvider.php';
|
require_once 'lib/Providers/Rng/HashRNGProvider.php';
|
||||||
@@ -24,7 +25,7 @@ class TwoFactorAuthTest extends PHPUnit_Framework_TestCase
|
|||||||
*/
|
*/
|
||||||
public function testConstructorThrowsOnInvalidDigits() {
|
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() {
|
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() {
|
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() {
|
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() {
|
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() {
|
public function testGetCodeReturnsCorrectResults() {
|
||||||
@@ -147,7 +148,7 @@ class TwoFactorAuthTest extends PHPUnit_Framework_TestCase
|
|||||||
*/
|
*/
|
||||||
public function testGetCodeThrowsOnInvalidBase32String1() {
|
public function testGetCodeThrowsOnInvalidBase32String1() {
|
||||||
$tfa = new TwoFactorAuth('Test');
|
$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() {
|
public function testGetCodeThrowsOnInvalidBase32String2() {
|
||||||
$tfa = new TwoFactorAuth('Test');
|
$tfa = new TwoFactorAuth('Test');
|
||||||
$result = $tfa->getCode('mzxw6==='); //Lowercase
|
$tfa->getCode('mzxw6==='); //Lowercase
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testKnownBase32DecodeTestVectors() {
|
public function testKnownBase32DecodeTestVectors() {
|
||||||
@@ -242,7 +243,62 @@ class TwoFactorAuthTest extends PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals('47863826', $tfa->getCode($secret, 20000000000));
|
$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) {
|
private function DecodeDataUri($datauri) {
|
||||||
if (preg_match('/data:(?P<mimetype>[\w\.\-\/]+);(?P<encoding>\w+),(?P<data>.*)/', $datauri, $m) === 1) {
|
if (preg_match('/data:(?P<mimetype>[\w\.\-\/]+);(?P<encoding>\w+),(?P<data>.*)/', $datauri, $m) === 1) {
|
||||||
return array(
|
return array(
|
||||||
|
|||||||
Reference in New Issue
Block a user