mirror of
https://github.com/RobThree/TwoFactorAuth.git
synced 2026-08-21 23:41:50 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 83ad9fb4e4 | |||
| 93fc6355d4 | |||
| a77e7d8223 | |||
| 673223a2fc | |||
| d450ba432d | |||
| d22329375f | |||
| b1ecc9afbc | |||
| c9bfe0519a | |||
| 404f147aa7 | |||
| 18888f83ee | |||
| 3d04717346 |
+4
-1
@@ -183,4 +183,7 @@ UpgradeLog*.htm
|
||||
FakesAssemblies/
|
||||
|
||||
# Composer
|
||||
/vendor
|
||||
/vendor
|
||||
|
||||
# .vs
|
||||
.vs/
|
||||
+10
-3
@@ -1,11 +1,18 @@
|
||||
language: php
|
||||
|
||||
dist: trusty
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.3
|
||||
dist: precise
|
||||
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7
|
||||
- 7.0
|
||||
- 7.1
|
||||
- hhvm
|
||||
|
||||
script: phpunit --coverage-text tests
|
||||
script:
|
||||
- if [[ "$TRAVIS_PHP_VERSION" == '5.6' ]]; then phpunit --coverage-text tests ; fi
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@ PHP library for [two-factor (or multi-factor) authentication](http://en.wikipedi
|
||||
|
||||
## Requirements
|
||||
|
||||
* 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.0, 7.1 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.
|
||||
* [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.
|
||||
|
||||
@@ -180,11 +180,11 @@ You can easily implement your own `RNGProvider` by simply implementing the `IRNG
|
||||
|
||||
### Time providers
|
||||
|
||||
Another set of providers in this library are the Time Providers; this library provides three 'built-in' ones. The default Time Provider used is the [`LocalMachineTimeProvider`](lib/Providers/Time/LocalMachineTimeProvider.php); this provider simply returns the output of `Time()` and is *highly recommended* as default provider. The [`HttpTimeProvider`](lib/Providers/Time/HttpTimeProvider.php) executes a `HEAD` request against a given webserver (default: google.com) and tries to extract the `Date:`-HTTP header and returns it's date. Other url's/domains can be used by specifying the url in the constructor. The final Time Provider is the [`ConvertUnixTimeDotComTimeProvider`](lib/Providers/Time/ConvertUnixTimeDotComTimeProvider.php) which does a HTTP request to `convert-unix-time.com/api` and decodes the `JSON` result to retrieve the time.
|
||||
Another set of providers in this library are the Time Providers; this library provides three 'built-in' ones. The default Time Provider used is the [`LocalMachineTimeProvider`](lib/Providers/Time/LocalMachineTimeProvider.php); this provider simply returns the output of `Time()` and is *highly recommended* as default provider. The [`HttpTimeProvider`](lib/Providers/Time/HttpTimeProvider.php) executes a `HEAD` request against a given webserver (default: google.com) and tries to extract the `Date:`-HTTP header and returns it's date. Other url's/domains can be used by specifying the url in the constructor. The final Time Provider is the [`NTPTimeProvider`](lib/Providers/Time/NTPTimeProvider.php) which does an NTP request to a specified NTP server.
|
||||
|
||||
You can easily implement your own `TimeProvider` by simply implementing the `ITimeProvider` interface.
|
||||
|
||||
As to *why* these Time Providers are implemented: it allows the TwoFactorAuth library to ensure the hosts time is correct (or rather: within a margin). You can use the `ensureCorrectTime()` method to ensure the hosts time is correct. By default this method will compare the hosts time (returned by calling `time()` on the `LocalMachineTimeProvider`) to Google's and convert-unix-time.com's current time. You can pass an array of `ITimeProvider`s and specify the `leniency` (second argument) allowed (default: 5 seconds). The method will throw when the TwoFactorAuth's timeprovider (which can be any `ITimeProvider`, see constructor) differs more than the given amount of seconds from any of the given `ITimeProviders`. We advise to call this method sparingly when relying on 3rd parties (which both the `HttpTimeProvider` and `ConvertUnixTimeDotComTimeProvider` do) or, if you need to ensure time is correct on a (very) regular basis to implement an `ITimeProvider` that is more efficient than the 'built-in' ones (like use a GPS signal). The `ensureCorrectTime()` method is mostly to be used to make sure the server is configured correctly.
|
||||
As to *why* these Time Providers are implemented: it allows the TwoFactorAuth library to ensure the hosts time is correct (or rather: within a margin). You can use the `ensureCorrectTime()` method to ensure the hosts time is correct. By default this method will compare the hosts time (returned by calling `time()` on the `LocalMachineTimeProvider`) to Google's and convert-unix-time.com's current time. You can pass an array of `ITimeProvider`s and specify the `leniency` (second argument) allowed (default: 5 seconds). The method will throw when the TwoFactorAuth's timeprovider (which can be any `ITimeProvider`, see constructor) differs more than the given amount of seconds from any of the given `ITimeProviders`. We advise to call this method sparingly when relying on 3rd parties (which both the `HttpTimeProvider` and `NTPTimeProvider` do) or, if you need to ensure time is correct on a (very) regular basis to implement an `ITimeProvider` that is more efficient than the 'built-in' ones (like use a GPS signal). The `ensureCorrectTime()` method is mostly to be used to make sure the server is configured correctly.
|
||||
|
||||
## Integrations
|
||||
|
||||
|
||||
@@ -38,10 +38,10 @@
|
||||
<Compile Include="lib\Providers\Rng\OpenSSLRNGProvider.php" />
|
||||
<Compile Include="lib\Providers\Rng\HashRNGProvider.php" />
|
||||
<Compile Include="lib\Providers\Rng\RNGException.php" />
|
||||
<Compile Include="lib\Providers\Time\ConvertUnixTimeDotComTimeProvider.php" />
|
||||
<Compile Include="lib\Providers\Time\HttpTimeProvider.php" />
|
||||
<Compile Include="lib\Providers\Time\ITimeProvider.php" />
|
||||
<Compile Include="lib\Providers\Time\LocalMachineTimeProvider.php" />
|
||||
<Compile Include="lib\Providers\Time\NTPTimeProvider.php" />
|
||||
<Compile Include="lib\Providers\Time\TimeException.php" />
|
||||
<Compile Include="lib\TwoFactorAuth.php" />
|
||||
<Compile Include=".gitignore" />
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "robthree/twofactorauth",
|
||||
"description": "Two Factor Authentication",
|
||||
"version": "1.6",
|
||||
"version": "1.6.2",
|
||||
"type": "library",
|
||||
"keywords": [ "Authentication", "Two Factor Authentication", "Multi Factor Authentication", "TFA", "MFA", "PHP", "Authenticator", "Authy" ],
|
||||
"homepage": "https://github.com/RobThree/TwoFactorAuth",
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace RobThree\Auth\Providers\Time;
|
||||
|
||||
class ConvertUnixTimeDotComTimeProvider implements ITimeProvider
|
||||
{
|
||||
public function getTime() {
|
||||
$json = @json_decode(
|
||||
@file_get_contents('http://www.convert-unix-time.com/api?timestamp=now')
|
||||
);
|
||||
if ($json === null || !is_int($json->timestamp))
|
||||
throw new \TimeException('Unable to retrieve time from convert-unix-time.com');
|
||||
return $json->timestamp;
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,8 @@ class HttpTimeProvider implements ITimeProvider
|
||||
'request_fulluri' => true,
|
||||
'header' => array(
|
||||
'Connection: close',
|
||||
'User-agent: TwoFactorAuth HttpTimeProvider (https://github.com/RobThree/TwoFactorAuth)'
|
||||
'User-agent: TwoFactorAuth HttpTimeProvider (https://github.com/RobThree/TwoFactorAuth)',
|
||||
'Cache-Control: no-cache'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace RobThree\Auth\Providers\Time;
|
||||
|
||||
/**
|
||||
* Takes the time from any NTP server
|
||||
*/
|
||||
class NTPTimeProvider implements ITimeProvider
|
||||
{
|
||||
public $host;
|
||||
public $port;
|
||||
public $timeout;
|
||||
|
||||
function __construct($host = 'pool.ntp.org', $port = 123, $timeout = 1)
|
||||
{
|
||||
$this->host = $host;
|
||||
|
||||
if (!is_int($port) || $port <= 0 || $port > 65535)
|
||||
throw new \TimeException('Port must be 0 < port < 65535');
|
||||
$this->port = $port;
|
||||
|
||||
if (!is_int($timeout) || $timeout < 0)
|
||||
throw new \TimeException('Timeout must be >= 0');
|
||||
$this->timeout = $timeout;
|
||||
}
|
||||
|
||||
public function getTime() {
|
||||
try {
|
||||
/* Create a socket and connect to NTP server */
|
||||
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
|
||||
socket_connect($sock, $this->host, $this->port);
|
||||
|
||||
/* Send request */
|
||||
$msg = "\010" . str_repeat("\0", 47);
|
||||
socket_send($sock, $msg, strlen($msg), 0);
|
||||
|
||||
/* Receive response and close socket */
|
||||
socket_recv($sock, $recv, 48, MSG_WAITALL);
|
||||
socket_close($sock);
|
||||
|
||||
/* Interpret response */
|
||||
$data = unpack('N12', $recv);
|
||||
$timestamp = sprintf('%u', $data[9]);
|
||||
|
||||
/* NTP is number of seconds since 0000 UT on 1 January 1900 Unix time is seconds since 0000 UT on 1 January 1970 */
|
||||
return $timestamp - 2208988800;
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
throw new \TimeException(sprintf('Unable to retrieve time from %s (%s)', $this->host, $ex->getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,16 +79,17 @@ class TwoFactorAuth
|
||||
/**
|
||||
* Check if the code is correct. This will accept codes starting from ($discrepancy * $period) sec ago to ($discrepancy * period) sec from now
|
||||
*/
|
||||
public function verifyCode($secret, $code, $discrepancy = 1, $time = null)
|
||||
public function verifyCode($secret, $code, $discrepancy = 1, $time = null, &$timeslice = 0)
|
||||
{
|
||||
$result = false;
|
||||
$timetamp = $this->getTime($time);
|
||||
|
||||
// To keep safe from timing-attachs we iterate *all* possible codes even though we already may have verified a code is correct
|
||||
for ($i = -$discrepancy; $i <= $discrepancy; $i++)
|
||||
$result |= $this->codeEquals($this->getCode($secret, $timetamp + ($i * $this->period)), $code);
|
||||
for ($i = -$discrepancy; $i <= $discrepancy; $i++) {
|
||||
$ts = $timetamp + ($i * $this->period);
|
||||
$timeslice += $this->codeEquals($this->getCode($secret, $ts), $code) ? $ts : 0;
|
||||
}
|
||||
|
||||
return (bool)$result;
|
||||
return $timeslice > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,7 +135,7 @@ class TwoFactorAuth
|
||||
|
||||
if ($timeproviders == null)
|
||||
$timeproviders = array(
|
||||
new Providers\Time\ConvertUnixTimeDotComTimeProvider(),
|
||||
new Providers\Time\NTPTimeProvider(),
|
||||
new Providers\Time\HttpTimeProvider()
|
||||
);
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ require_once 'lib/Providers/Rng/RNGException.php';
|
||||
require_once 'lib/Providers/Time/ITimeProvider.php';
|
||||
require_once 'lib/Providers/Time/LocalMachineTimeProvider.php';
|
||||
require_once 'lib/Providers/Time/HttpTimeProvider.php';
|
||||
require_once 'lib/Providers/Time/ConvertUnixTimeDotComTimeProvider.php';
|
||||
require_once 'lib/Providers/Time/NTPTimeProvider.php';
|
||||
require_once 'lib/Providers/Time/TimeException.php';
|
||||
|
||||
use RobThree\Auth\TwoFactorAuth;
|
||||
@@ -124,7 +124,8 @@ class TwoFactorAuthTest extends PHPUnit_Framework_TestCase
|
||||
public function testEnsureAllTimeProvidersReturnCorrectTime() {
|
||||
$tfa = new TwoFactorAuth('Test', 6, 30, 'sha1');
|
||||
$tfa->ensureCorrectTime(array(
|
||||
new RobThree\Auth\Providers\Time\ConvertUnixTimeDotComTimeProvider(),
|
||||
new RobThree\Auth\Providers\Time\NTPTimeProvider(), // Uses pool.ntp.org by default
|
||||
new RobThree\Auth\Providers\Time\NTPTimeProvider('time.windows.com'),
|
||||
new RobThree\Auth\Providers\Time\HttpTimeProvider(), // Uses google.com by default
|
||||
new RobThree\Auth\Providers\Time\HttpTimeProvider('https://github.com'),
|
||||
new RobThree\Auth\Providers\Time\HttpTimeProvider('https://yahoo.com'),
|
||||
@@ -150,6 +151,27 @@ class TwoFactorAuthTest extends PHPUnit_Framework_TestCase
|
||||
$this->assertEquals(true , $tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 2, 1426847205 - 65)); //Test discrepancy
|
||||
}
|
||||
|
||||
public function testVerifyCorrectTimeSliceIsReturned() {
|
||||
$tfa = new TwoFactorAuth('Test', 6, 30);
|
||||
|
||||
// We test with discrepancy 3 (so total of 7 codes: c-3, c-2, c-1, c, c+1, c+2, c+3
|
||||
// Ensure each corresponding timeslice is returned correctly
|
||||
$this->assertEquals(true, $tfa->verifyCode('VMR466AB62ZBOKHE', '534113', 3, 1426847190, $timeslice1));
|
||||
$this->assertEquals(1426847100, $timeslice1);
|
||||
$this->assertEquals(true, $tfa->verifyCode('VMR466AB62ZBOKHE', '819652', 3, 1426847190, $timeslice2));
|
||||
$this->assertEquals(1426847130, $timeslice2);
|
||||
$this->assertEquals(true, $tfa->verifyCode('VMR466AB62ZBOKHE', '915954', 3, 1426847190, $timeslice3));
|
||||
$this->assertEquals(1426847160, $timeslice3);
|
||||
$this->assertEquals(true, $tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 3, 1426847190, $timeslice4));
|
||||
$this->assertEquals(1426847190, $timeslice4);
|
||||
$this->assertEquals(true, $tfa->verifyCode('VMR466AB62ZBOKHE', '348401', 3, 1426847190, $timeslice5));
|
||||
$this->assertEquals(1426847220, $timeslice5);
|
||||
$this->assertEquals(true, $tfa->verifyCode('VMR466AB62ZBOKHE', '648525', 3, 1426847190, $timeslice6));
|
||||
$this->assertEquals(1426847250, $timeslice6);
|
||||
$this->assertEquals(true, $tfa->verifyCode('VMR466AB62ZBOKHE', '170645', 3, 1426847190, $timeslice7));
|
||||
$this->assertEquals(1426847280, $timeslice7);
|
||||
}
|
||||
|
||||
public function testTotpUriIsCorrect() {
|
||||
$qr = new TestQrProvider();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user