Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorThomas Steur <tsteur@users.noreply.github.com>2018-12-03 06:27:29 +0300
committerGitHub <noreply@github.com>2018-12-03 06:27:29 +0300
commit284bdc0816dd2eff4010e4be42812ff3cc7e25e1 (patch)
tree88c60d0e72bae97b5467c5ad7693a64dd477bf3e /libs
parente679e0383496383b00f95fd5fd0e42eed4ca49fe (diff)
Implement Two Factor Authentication (#13670)
Diffstat (limited to 'libs')
-rw-r--r--libs/Authenticator/LICENSE.md22
-rw-r--r--libs/Authenticator/README.md85
-rw-r--r--libs/Authenticator/TwoFactorAuthenticator.php195
3 files changed, 302 insertions, 0 deletions
diff --git a/libs/Authenticator/LICENSE.md b/libs/Authenticator/LICENSE.md
new file mode 100644
index 0000000000..530a37b0ab
--- /dev/null
+++ b/libs/Authenticator/LICENSE.md
@@ -0,0 +1,22 @@
+Copyright (c) 2012, Michael Kliewe All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/libs/Authenticator/README.md b/libs/Authenticator/README.md
new file mode 100644
index 0000000000..fee7a28979
--- /dev/null
+++ b/libs/Authenticator/README.md
@@ -0,0 +1,85 @@
+Google Authenticator PHP class
+==============================
+
+* Copyright (c) 2012-2016, [http://www.phpgangsta.de](http://www.phpgangsta.de)
+* Author: Michael Kliewe, [@PHPGangsta](http://twitter.com/PHPGangsta) and [contributors](https://github.com/PHPGangsta/GoogleAuthenticator/graphs/contributors)
+* Licensed under the BSD License.
+
+[![Build Status](https://travis-ci.org/PHPGangsta/GoogleAuthenticator.png?branch=master)](https://travis-ci.org/PHPGangsta/GoogleAuthenticator)
+
+This PHP class can be used to interact with the Google Authenticator mobile app for 2-factor-authentication. This class
+can generate secrets, generate codes, validate codes and present a QR-Code for scanning the secret. It implements TOTP
+according to [RFC6238](https://tools.ietf.org/html/rfc6238)
+
+For a secure installation you have to make sure that used codes cannot be reused (replay-attack). You also need to
+limit the number of verifications, to fight against brute-force attacks. For example you could limit the amount of
+verifications to 10 tries within 10 minutes for one IP address (or IPv6 block). It depends on your environment.
+
+Usage:
+------
+
+See following example:
+
+```php
+<?php
+require_once 'PHPGangsta/GoogleAuthenticator.php';
+
+$ga = new PHPGangsta_GoogleAuthenticator();
+$secret = $ga->createSecret();
+echo "Secret is: ".$secret."\n\n";
+
+$qrCodeUrl = $ga->getQRCodeGoogleUrl('Blog', $secret);
+echo "Google Charts URL for the QR-Code: ".$qrCodeUrl."\n\n";
+
+$oneCode = $ga->getCode($secret);
+echo "Checking Code '$oneCode' and Secret '$secret':\n";
+
+$checkResult = $ga->verifyCode($secret, $oneCode, 2); // 2 = 2*30sec clock tolerance
+if ($checkResult) {
+ echo 'OK';
+} else {
+ echo 'FAILED';
+}
+```
+Running the script provides the following output:
+```
+Secret is: OQB6ZZGYHCPSX4AK
+
+Google Charts URL for the QR-Code: https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/infoATphpgangsta.de%3Fsecret%3DOQB6ZZGYHCPSX4AK
+
+Checking Code '848634' and Secret 'OQB6ZZGYHCPSX4AK':
+OK
+```
+
+Installation:
+-------------
+
+- Use [Composer](https://getcomposer.org/doc/01-basic-usage.md) to
+ install the package
+
+- From project root directory execute following
+
+```composer install```
+
+- [Composer](https://getcomposer.org/doc/01-basic-usage.md) will take care of autoloading
+ the library. Just include the following at the top of your file
+
+ `require_once __DIR__ . '/../vendor/autoload.php';`
+
+Run Tests:
+----------
+
+- All tests are inside `tests` folder.
+- Execute `composer install` and then run the tests from project root
+ directory
+- Run as `phpunit tests` from the project root directory
+
+
+ToDo:
+-----
+- ??? What do you need?
+
+Notes:
+------
+
+If you like this script or have some features to add: contact me, visit my blog, fork this project, send pull requests, you know how it works. \ No newline at end of file
diff --git a/libs/Authenticator/TwoFactorAuthenticator.php b/libs/Authenticator/TwoFactorAuthenticator.php
new file mode 100644
index 0000000000..fc0b962856
--- /dev/null
+++ b/libs/Authenticator/TwoFactorAuthenticator.php
@@ -0,0 +1,195 @@
+<?php
+/**
+ * PHP Class for handling Google Authenticator 2-factor authentication
+ *
+ * @author Michael Kliewe
+ * @copyright 2012 Michael Kliewe
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link http://www.phpgangsta.de/
+ *
+ * small adjustments by @sgiehl / matomo.org
+ * - renamed class
+ * - removed method getQRCodeGoogleUrl
+ */
+
+class TwoFactorAuthenticator
+{
+ protected $_codeLength = 6;
+
+ /**
+ * Create new secret.
+ * 16 characters, randomly chosen from the allowed base32 characters.
+ *
+ * @param int $secretLength
+ * @return string
+ */
+ public function createSecret($secretLength = 16)
+ {
+ $validChars = $this->_getBase32LookupTable();
+ unset($validChars[32]);
+
+ $secret = '';
+ for ($i = 0; $i < $secretLength; $i++) {
+ $secret .= $validChars[array_rand($validChars)];
+ }
+ return $secret;
+ }
+
+ /**
+ * Calculate the code, with given secret and point in time
+ *
+ * @param string $secret
+ * @param int|null $timeSlice
+ * @return string
+ */
+ public function getCode($secret, $timeSlice = null)
+ {
+ if ($timeSlice === null) {
+ $timeSlice = floor(time() / 30);
+ }
+
+ $secretkey = $this->_base32Decode($secret);
+
+ // Pack time into binary string
+ $time = chr(0).chr(0).chr(0).chr(0).pack('N*', $timeSlice);
+ // Hash it with users secret key
+ $hm = hash_hmac('SHA1', $time, $secretkey, true);
+ // Use last nipple of result as index/offset
+ $offset = ord(substr($hm, -1)) & 0x0F;
+ // grab 4 bytes of the result
+ $hashpart = substr($hm, $offset, 4);
+
+ // Unpak binary value
+ $value = unpack('N', $hashpart);
+ $value = $value[1];
+ // Only 32 bits
+ $value = $value & 0x7FFFFFFF;
+
+ $modulo = pow(10, $this->_codeLength);
+ return str_pad($value % $modulo, $this->_codeLength, '0', STR_PAD_LEFT);
+ }
+
+ /**
+ * Check if the code is correct. This will accept codes starting from $discrepancy*30sec ago to $discrepancy*30sec from now
+ *
+ * @param string $secret
+ * @param string $code
+ * @param int $discrepancy This is the allowed time drift in 30 second units (8 means 4 minutes before or after)
+ * @param int|null $currentTimeSlice time slice if we want use other that time()
+ * @return bool
+ */
+ public function verifyCode($secret, $code, $discrepancy = 1, $currentTimeSlice = null)
+ {
+ if ($currentTimeSlice === null) {
+ $currentTimeSlice = floor(time() / 30);
+ }
+
+ for ($i = -$discrepancy; $i <= $discrepancy; $i++) {
+ $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i);
+ if ($calculatedCode == $code ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Set the code length, should be >=6
+ *
+ * @param int $length
+ * @return self
+ */
+ public function setCodeLength($length)
+ {
+ $this->_codeLength = $length;
+ return $this;
+ }
+
+ /**
+ * Helper class to decode base32
+ *
+ * @param $secret
+ * @return bool|string
+ */
+ protected function _base32Decode($secret)
+ {
+ if (empty($secret)) return '';
+
+ $base32chars = $this->_getBase32LookupTable();
+ $base32charsFlipped = array_flip($base32chars);
+
+ $paddingCharCount = substr_count($secret, $base32chars[32]);
+ $allowedValues = array(6, 4, 3, 1, 0);
+ if (!in_array($paddingCharCount, $allowedValues)) return false;
+ for ($i = 0; $i < 4; $i++){
+ if ($paddingCharCount == $allowedValues[$i] &&
+ substr($secret, -($allowedValues[$i])) != str_repeat($base32chars[32], $allowedValues[$i])) return false;
+ }
+ $secret = str_replace('=','', $secret);
+ $secret = str_split($secret);
+ $binaryString = "";
+ for ($i = 0; $i < count($secret); $i = $i+8) {
+ $x = "";
+ if (!in_array($secret[$i], $base32chars)) return false;
+ for ($j = 0; $j < 8; $j++) {
+ $x .= str_pad(base_convert(@$base32charsFlipped[@$secret[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT);
+ }
+ $eightBits = str_split($x, 8);
+ for ($z = 0; $z < count($eightBits); $z++) {
+ $binaryString .= ( ($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48 ) ? $y:"";
+ }
+ }
+ return $binaryString;
+ }
+
+ /**
+ * Helper class to encode base32
+ *
+ * @param string $secret
+ * @param bool $padding
+ * @return string
+ */
+ protected function _base32Encode($secret, $padding = true)
+ {
+ if (empty($secret)) return '';
+
+ $base32chars = $this->_getBase32LookupTable();
+
+ $secret = str_split($secret);
+ $binaryString = "";
+ for ($i = 0; $i < count($secret); $i++) {
+ $binaryString .= str_pad(base_convert(ord($secret[$i]), 10, 2), 8, '0', STR_PAD_LEFT);
+ }
+ $fiveBitBinaryArray = str_split($binaryString, 5);
+ $base32 = "";
+ $i = 0;
+ while ($i < count($fiveBitBinaryArray)) {
+ $base32 .= $base32chars[base_convert(str_pad($fiveBitBinaryArray[$i], 5, '0'), 2, 10)];
+ $i++;
+ }
+ if ($padding && ($x = strlen($binaryString) % 40) != 0) {
+ if ($x == 8) $base32 .= str_repeat($base32chars[32], 6);
+ elseif ($x == 16) $base32 .= str_repeat($base32chars[32], 4);
+ elseif ($x == 24) $base32 .= str_repeat($base32chars[32], 3);
+ elseif ($x == 32) $base32 .= $base32chars[32];
+ }
+ return $base32;
+ }
+
+ /**
+ * Get array with all 32 characters for decoding from/encoding to base32
+ *
+ * @return array
+ */
+ protected function _getBase32LookupTable()
+ {
+ return array(
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 7
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23
+ 'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31
+ '=' // padding char
+ );
+ }
+} \ No newline at end of file