Compare commits

...

14 Commits

Author SHA1 Message Date
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
Rob Janssen 60c4ec7fb4 Update composer.json 2015-11-20 14:03:57 +01:00
Rob Janssen c8ec3f624f Update README.md 2015-11-20 00:31:51 +01:00
Rob Janssen 80f00338a2 Update README.md 2015-11-20 00:17:24 +01:00
Rob Janssen 53a184f85c Update README.md 2015-11-19 21:35:51 +01:00
Rob Janssen c3c762a8f3 Update README.md 2015-11-19 02:29:39 +01:00
Rob Janssen 69c1597c71 Update LICENSE 2015-11-19 02:29:07 +01:00
Rob Janssen d1878f2e5c * Class -> Library 2015-11-19 02:03:53 +01:00
RobThree e837caa9ce * Force VerifyCode() to return bool 2015-07-16 15:43:45 +02:00
Rob Janssen 0332a61b67 Update README.md 2015-05-07 12:43:42 +02:00
Rob Janssen cff5dadb79 * Typo 2015-05-06 11:21:53 +02:00
Rob Janssen 1647e46ee3 * Removed incorrect statement 2015-05-06 11:19:28 +02:00
9 changed files with 96 additions and 21 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2014 Rob Janssen
Copyright (c) 2014-2015 Rob Janssen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+10 -10
View File
@@ -1,8 +1,8 @@
# ![Logo](https://raw.githubusercontent.com/RobThree/TwoFactorAuth/master/logo.png) TwoFactorAuth class for PHP
# ![Logo](https://raw.githubusercontent.com/RobThree/TwoFactorAuth/master/logo.png) PHP library for Two Factor Authentication
[![Build status](https://img.shields.io/travis/RobThree/TwoFactorAuth.svg?style=flat-square)](https://travis-ci.org/RobThree/TwoFactorAuth/) [![Latest Stable Version](https://img.shields.io/packagist/v/robthree/twofactorauth.svg?style=flat-square)](https://packagist.org/packages/robthree/twofactorauth) [![License](https://img.shields.io/packagist/l/robthree/twofactorauth.svg?style=flat-square)](LICENSE) [![Downloads](https://img.shields.io/packagist/dt/robthree/twofactorauth.svg?style=flat-square)](https://packagist.org/packages/robthree/twofactorauth) [![HHVM Status](https://img.shields.io/hhvm/RobThree/TwoFactorAuth.svg?style=flat-square)](http://hhvm.h4cc.de/package/robthree/twofactorauth) [![Code Climate](https://img.shields.io/codeclimate/github/RobThree/TwoFactorAuth.svg?style=flat-square)](https://codeclimate.com/github/RobThree/TwoFactorAuth) [![PayPal donate button](http://img.shields.io/badge/paypal-donate-orange.svg?style=flat-square)](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 library 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)'. There's a [.Net implementation](https://github.com/RobThree/TwoFactorAuth.Net) of this library as well.
<p align="center">
<img src="https://raw.githubusercontent.com/RobThree/TwoFactorAuth/master/multifactorauthforeveryone.png">
@@ -10,7 +10,7 @@ PHP class for [two-factor (or multi-factor) authentication](http://en.wikipedia.
## Requirements
* Tested on PHP 5.3, 5.4, 5.5 and 5.6
* 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.
@@ -48,7 +48,7 @@ These parameters are all '`write once`'; the class will, for it's lifetime, use
### Step 1: Set up secret shared key
When a user wants to setup two-factor auth (or, more correctly, multi-factor auth) you need to create a secret. This will be your **shared** (this will be the `one-time` in [TOTP](http://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm)) **secret**. This secret will need to be entered by the user in their app. This can be done manually, in which case you simply display the secret and have the user type it in the app:
When a user wants to setup two-factor auth (or, more correctly, multi-factor auth) you need to create a secret. This will be your **shared secret**. This secret will need to be entered by the user in their app. This can be done manually, in which case you simply display the secret and have the user type it in the app:
````php
$secret = $tfa->createSecret();
@@ -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 code 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
@@ -109,9 +109,9 @@ verifyCode($secret, $code, $discrepancy = 1, $time = null)
### QR-code providers
As mentioned before, this class comes with three 'built-in' QR-code providers. This chapter will touch the subject a bit but most of it should be self-explanatory. The `TwoFactorAuth`-class accepts a `$qrcodeprovider` parameter which lets you specify a built-in or custom QR-code provider. All three built-in providers do a simple HTTP request to retrieve an image using cURL and implement the [`IQRCodeProvider`](lib/Providers/Qr/IQRCodeProvider.php) interface which is all you need to implement to write your own QR-code provider.
As mentioned before, this library comes with three 'built-in' QR-code providers. This chapter will touch the subject a bit but most of it should be self-explanatory. The `TwoFactorAuth`-class accepts a `$qrcodeprovider` parameter which lets you specify a built-in or custom QR-code provider. All three built-in providers do a simple HTTP request to retrieve an image using cURL and implement the [`IQRCodeProvider`](lib/Providers/Qr/IQRCodeProvider.php) interface which is all you need to implement to write your own QR-code provider.
The default provider is the [`GoogleQRCodeProvider`](lib/Providers/Qr/GoogleQRCodeProvider.php) which uses the [Google Chart Tools](https://developers.google.com/chart/infographics/docs/qr_codes) to render QR-codes. Then we have the [`QRServerProvider`](lib/Providers/Qr/QRServerProvider.php) which uses the [goqr.me API](http://goqr.me/api/doc/create-qr-code/) and finally we have the [`QRicketProvider`](lib/Providers/Qr/QRicketProvider.php) which uses the [QRickit API](http://qrickit.com/qrickit_apps/qrickit_api.php). All three inherit from a common (abstract) base-class named [`BaseHTTPQRCodeProvider`](lib/Providers/Qr/BaseHTTPQRCodeProvider.php) because all three share the same functionality: retrieve an image from a 3rd party over HTTP. All three classes have constructors that allow you to tweak some settings and most, if not all, arguments should speak for themselves. If you're not sure which values are supported, click the links in this paragraph for documentation on the API's that are utilized by these classes.
The default provider is the [`GoogleQRCodeProvider`](lib/Providers/Qr/GoogleQRCodeProvider.php) which uses the [Google Chart Tools](https://developers.google.com/chart/infographics/docs/qr_codes) to render QR-codes. Then we have the [`QRServerProvider`](lib/Providers/Qr/QRServerProvider.php) which uses the [goqr.me API](http://goqr.me/api/doc/create-qr-code/) and finally we have the [`QRicketProvider`](lib/Providers/Qr/QRicketProvider.php) which uses the [QRickit API](http://qrickit.com/qrickit_apps/qrickit_api.php). All three inherit from a common (abstract) baseclass named [`BaseHTTPQRCodeProvider`](lib/Providers/Qr/BaseHTTPQRCodeProvider.php) because all three share the same functionality: retrieve an image from a 3rd party over HTTP. All three classes have constructors that allow you to tweak some settings and most, if not all, arguments should speak for themselves. If you're not sure which values are supported, click the links in this paragraph for documentation on the API's that are utilized by these classes.
If you don't like any of the built-in classes because you don't want to rely on external resources for example or because you're paranoid about sending the TOTP secret to these 3rd parties (which is useless to them since they miss *at least one* other factor in the [MFA process](http://en.wikipedia.org/wiki/Multi-factor_authentication)), feel tree to implement your own. The `IQRCodeProvider` interface couldn't be any simpler. All you need to do is implement 2 methods:
@@ -166,12 +166,12 @@ Voilà. Couldn't make it any simpler.
### RNG providers
This class 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, be 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 [`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.
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).
## License
Licensed under MIT license. See LICENSE file for details.
Licensed under MIT license. See [LICENSE](https://raw.githubusercontent.com/RobThree/TwoFactorAuth/master/LICENSE) for details.
[Logo / icon](http://www.iconmay.com/Simple/Travel_and_Tourism_Part_2/luggage_lock_safety_baggage_keys_cylinder_lock_hotel_travel_tourism_luggage_lock_icon_465) under CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
[Logo / icon](http://www.iconmay.com/Simple/Travel_and_Tourism_Part_2/luggage_lock_safety_baggage_keys_cylinder_lock_hotel_travel_tourism_luggage_lock_icon_465) under CC0 1.0 Universal (CC0 1.0) Public Domain Dedication ([Archived page](http://riii.nl/tm7ap))
+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.1",
"version": "1.4",
"type": "library",
"keywords": [ "Authentication", "Two Factor Authentication", "Multi Factor Authentication", "TFA", "MFA", "PHP", "Authenticator", "Authy" ],
"homepage": "https://github.com/RobThree/TwoFactorAuth",
+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;
}
}
+4 -2
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();
@@ -108,7 +110,7 @@ class TwoFactorAuth
for ($i = -$discrepancy; $i <= $discrepancy; $i++)
$result |= $this->codeEquals($this->getCode($secret, $timetamp + ($i * $this->period)), $code);
return $result;
return (bool)$result;
}
/**
+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(