mirror of
https://github.com/RobThree/TwoFactorAuth.git
synced 2026-08-26 19:16:49 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 52cbcf7579 | |||
| 1f9008c217 | |||
| 955e9b0b03 | |||
| 1d0a9432e6 | |||
| 3b3e723bea | |||
| f1a729c9ed | |||
| 98d3f2a21b | |||
| 8976cf138e |
+1
-1
@@ -8,4 +8,4 @@ php:
|
|||||||
- 7
|
- 7
|
||||||
- hhvm
|
- hhvm
|
||||||
|
|
||||||
script: phpunit tests
|
script: phpunit --coverage-text tests
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#  TwoFactorAuth class for PHP
|
#  TwoFactorAuth class for PHP
|
||||||
|
|
||||||
[](https://travis-ci.org/RobThree/TwoFactorAuth/) [](https://packagist.org/packages/robthree/twofactorauth) [](LICENSE) [](https://packagist.org/packages/robthree/twofactorauth) [](http://hhvm.h4cc.de/package/robthree/twofactorauth) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6MB5M2SQLP636 "Keep me off the streets")
|
[](https://travis-ci.org/RobThree/TwoFactorAuth/) [](https://packagist.org/packages/robthree/twofactorauth) [](LICENSE) [](https://packagist.org/packages/robthree/twofactorauth) [](http://hhvm.h4cc.de/package/robthree/twofactorauth) [](https://codeclimate.com/github/RobThree/TwoFactorAuth) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6MB5M2SQLP636 "Keep me off the streets")
|
||||||
|
|
||||||
PHP class for [two-factor (or multi-factor) authentication](http://en.wikipedia.org/wiki/Multi-factor_authentication) using [TOTP](http://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm) and [QR-codes](http://en.wikipedia.org/wiki/QR_code). Inspired by, based on but most importantly an *improvement* on '[PHPGangsta/GoogleAuthenticator](https://github.com/PHPGangsta/GoogleAuthenticator)'.
|
PHP class for [two-factor (or multi-factor) authentication](http://en.wikipedia.org/wiki/Multi-factor_authentication) using [TOTP](http://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm) and [QR-codes](http://en.wikipedia.org/wiki/QR_code). Inspired by, based on but most importantly an *improvement* on '[PHPGangsta/GoogleAuthenticator](https://github.com/PHPGangsta/GoogleAuthenticator)'.
|
||||||
|
|
||||||
@@ -16,12 +16,9 @@ PHP class for [two-factor (or multi-factor) authentication](http://en.wikipedia.
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
````json
|
Run the following command:
|
||||||
"require": {
|
|
||||||
"robthree/twofactorauth": "1.0"
|
`php composer.phar require robthree/twofactorauth`
|
||||||
}
|
|
||||||
````
|
|
||||||
And run `php composer update`
|
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "robthree/twofactorauth",
|
"name": "robthree/twofactorauth",
|
||||||
"description": "Two Factor Authentication",
|
"description": "Two Factor Authentication",
|
||||||
"version": "1.0",
|
"version": "1.1",
|
||||||
"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",
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
<body>
|
<body>
|
||||||
<ol>
|
<ol>
|
||||||
<?php
|
<?php
|
||||||
error_reporting(-1);
|
|
||||||
require_once 'loader.php';
|
require_once 'loader.php';
|
||||||
Loader::register('../lib','RobThree\\Auth');
|
Loader::register('../lib','RobThree\\Auth');
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ abstract class BaseHTTPQRCodeProvider implements IQRCodeProvider
|
|||||||
|
|
||||||
protected function getContent($url)
|
protected function getContent($url)
|
||||||
{
|
{
|
||||||
$ch = curl_init();
|
$curlhandle = curl_init();
|
||||||
|
|
||||||
curl_setopt_array($ch, array(
|
curl_setopt_array($curlhandle, array(
|
||||||
CURLOPT_URL => $url,
|
CURLOPT_URL => $url,
|
||||||
CURLOPT_FOLLOWLOCATION => true,
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
CURLOPT_MAXREDIRS => 3,
|
CURLOPT_MAXREDIRS => 3,
|
||||||
@@ -21,9 +21,9 @@ abstract class BaseHTTPQRCodeProvider implements IQRCodeProvider
|
|||||||
CURLOPT_SSL_VERIFYPEER => $this->verifyssl,
|
CURLOPT_SSL_VERIFYPEER => $this->verifyssl,
|
||||||
CURLOPT_USERAGENT => 'TwoFactorAuth'
|
CURLOPT_USERAGENT => 'TwoFactorAuth'
|
||||||
));
|
));
|
||||||
$data = curl_exec($ch);
|
$data = curl_exec($curlhandle);
|
||||||
|
|
||||||
curl_close($ch);
|
curl_close($curlhandle);
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+40
-21
@@ -87,11 +87,11 @@ class TwoFactorAuth
|
|||||||
{
|
{
|
||||||
$secretkey = $this->base32Decode($secret);
|
$secretkey = $this->base32Decode($secret);
|
||||||
|
|
||||||
$ts = "\0\0\0\0" . pack('N*', $this->getTimeSlice($this->getTime($time))); // Pack time into binary string
|
$timestamp = "\0\0\0\0" . pack('N*', $this->getTimeSlice($this->getTime($time))); // Pack time into binary string
|
||||||
$hm = hash_hmac($this->algorithm, $ts, $secretkey, true); // Hash it with users secret key
|
$hashhmac = hash_hmac($this->algorithm, $timestamp, $secretkey, true); // Hash it with users secret key
|
||||||
$hashpart = substr($hm, ord(substr($hm, -1)) & 0x0F, 4); // Use last nibble of result as index/offset and grab 4 bytes of the result
|
$hashpart = substr($hashhmac, ord(substr($hashhmac, -1)) & 0x0F, 4); // Use last nibble of result as index/offset and grab 4 bytes of the result
|
||||||
$value = unpack('N', $hashpart); // Unpack binary value
|
$value = unpack('N', $hashpart); // Unpack binary value
|
||||||
$value = $value[1] & 0x7FFFFFFF; // Drop MSB, keep only 31 bits
|
$value = $value[1] & 0x7FFFFFFF; // Drop MSB, keep only 31 bits
|
||||||
|
|
||||||
return str_pad($value % pow(10, $this->digits), $this->digits, '0', STR_PAD_LEFT);
|
return str_pad($value % pow(10, $this->digits), $this->digits, '0', STR_PAD_LEFT);
|
||||||
}
|
}
|
||||||
@@ -101,13 +101,32 @@ class TwoFactorAuth
|
|||||||
*/
|
*/
|
||||||
public function verifyCode($secret, $code, $discrepancy = 1, $time = null)
|
public function verifyCode($secret, $code, $discrepancy = 1, $time = null)
|
||||||
{
|
{
|
||||||
$t = $this->getTime($time);
|
$result = false;
|
||||||
for ($i = -$discrepancy; $i <= $discrepancy; $i++)
|
$timetamp = $this->getTime($time);
|
||||||
{
|
|
||||||
if (strcmp($this->getCode($secret, $t + ($i * $this->period)), $code) === 0)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timing-attack safe comparison of 2 codes (see http://blog.ircmaxell.com/2014/11/its-all-about-time.html)
|
||||||
|
*/
|
||||||
|
private function codeEquals($safe, $user) {
|
||||||
|
if (function_exists('hash_equals')) {
|
||||||
|
return hash_equals($safe, $user);
|
||||||
|
} else {
|
||||||
|
// In general, it's not possible to prevent length leaks. So it's OK to leak the length. The important part is that
|
||||||
|
// we don't leak information about the difference of the two strings.
|
||||||
|
if (strlen($safe)===strlen($user)) {
|
||||||
|
$result = 0;
|
||||||
|
for ($i = 0; $i < strlen($safe); $i++)
|
||||||
|
$result |= (ord($safe[$i]) ^ ord($user[$i]));
|
||||||
|
return $result === 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,19 +174,19 @@ class TwoFactorAuth
|
|||||||
if (preg_match('/[^'.preg_quote(self::$_base32dict).']/', $value) !== 0)
|
if (preg_match('/[^'.preg_quote(self::$_base32dict).']/', $value) !== 0)
|
||||||
throw new TwoFactorAuthException('Invalid base32 string');
|
throw new TwoFactorAuthException('Invalid base32 string');
|
||||||
|
|
||||||
$s = '';
|
$buffer = '';
|
||||||
foreach (str_split($value) as $c)
|
foreach (str_split($value) as $char)
|
||||||
{
|
{
|
||||||
if ($c !== '=')
|
if ($char !== '=')
|
||||||
$s .= str_pad(decbin(self::$_base32lookup[$c]), 5, 0, STR_PAD_LEFT);
|
$buffer .= str_pad(decbin(self::$_base32lookup[$char]), 5, 0, STR_PAD_LEFT);
|
||||||
}
|
}
|
||||||
$l = strlen($s);
|
$length = strlen($buffer);
|
||||||
$r = trim(chunk_split(substr($s, 0, $l - ($l % 8)), 8, ' '));
|
$blocks = trim(chunk_split(substr($buffer, 0, $length - ($length % 8)), 8, ' '));
|
||||||
|
|
||||||
$o = '';
|
$output = '';
|
||||||
foreach (explode(' ', $r) as $b)
|
foreach (explode(' ', $blocks) as $block)
|
||||||
$o .= chr(bindec(str_pad($b, 8, 0, STR_PAD_RIGHT)));
|
$output .= chr(bindec(str_pad($block, 8, 0, STR_PAD_RIGHT)));
|
||||||
|
|
||||||
return $o;
|
return $output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user