diff options
author | RobThree <rob@devcorner.nl> | 2014-09-24 23:33:15 +0200 |
---|---|---|
committer | RobThree <rob@devcorner.nl> | 2014-09-24 23:33:15 +0200 |
commit | 004739be326c49a3a92f32109e0578d60acd63e7 (patch) | |
tree | 36317c63b30e6b65762f630fc8877f7577fd2395 | |
parent | b50661c60acb9a48fa0c8fad688decb91b527f8e (diff) | |
download | TwoFactorAuth-004739be326c49a3a92f32109e0578d60acd63e7.zip TwoFactorAuth-004739be326c49a3a92f32109e0578d60acd63e7.tar.gz TwoFactorAuth-004739be326c49a3a92f32109e0578d60acd63e7.tar.bz2 |
* Using namespace(s): https://www.reddit.com/r/programming/comments/2gwqcr/multifactor_authentication_or_twofactor_auth_for/ckr9e1g
* Added simple "PSR-4" autoloader
* Classes and interfaces to separate files
* Updated README for namespaces
-rw-r--r-- | README.md | 16 | ||||
-rw-r--r-- | RobThree/TwoFactorAuth/Providers/BaseHTTPQRCodeProvider.php | 29 | ||||
-rw-r--r-- | RobThree/TwoFactorAuth/Providers/GoogleQRCodeProvider.php | 39 | ||||
-rw-r--r-- | RobThree/TwoFactorAuth/Providers/IQRCodeProvider.php | 9 | ||||
-rw-r--r-- | RobThree/TwoFactorAuth/Providers/QRServerProvider.php | 70 | ||||
-rw-r--r-- | RobThree/TwoFactorAuth/Providers/QRicketProvider.php | 53 | ||||
-rw-r--r-- | RobThree/TwoFactorAuth/TwoFactorAuth.php | 147 | ||||
-rw-r--r-- | TwoFactorAuth.phpproj | 12 | ||||
-rw-r--r-- | demo.php | 9 | ||||
-rw-r--r-- | loader.php | 50 | ||||
-rw-r--r-- | src/TwoFactorAuth.php | 329 |
11 files changed, 420 insertions, 343 deletions
@@ -18,8 +18,7 @@ Here are some code snippets that should help you get started... ````php // Start by including the TwoFactorAuth.php file which contains all you need (for now) -require_once 'src/TwoFactorAuth.php'; -$tfa = new TwoFactorAuth('My Company'); +$tfa = new RobThree\TwoFactorAuth\TwoFactorAuth('My Company'); ```` The TwoFactorAuth class constructor accepts 5 parameters (all optional): @@ -109,11 +108,13 @@ The `getMimeType()` method should return the [MIME type](http://en.wikipedia.org All you need to do is return the QR-code as binary image data and you're done. All parts of the `$qrtext` have been escaped for you (but note: you *may* need to escape the entire `$qrtext` just once more when passing the data to another server as GET-parameter). -Let's see if we can use [PHP QR Code](http://phpqrcode.sourceforge.net/) to implement our own, custom, no-3rd-parties-allowed-here, provider. We start with downloading the [required (single) file](https://github.com/t0k4rt/phpqrcode/blob/master/phpqrcode.php) and putting it in our `src/` directory where `TwoFactorAuth.php` is located as well. Now let's implement the provider: create another file named `myprovider.php` in the `src` directory and paste in this content: +Let's see if we can use [PHP QR Code](http://phpqrcode.sourceforge.net/) to implement our own, custom, no-3rd-parties-allowed-here, provider. We start with downloading the [required (single) file](https://github.com/t0k4rt/phpqrcode/blob/master/phpqrcode.php) and putting it in the directory where `TwoFactorAuth.php` is located as well. Now let's implement the provider: create another file named `myprovider.php` in the `Providers` directory and paste in this content: ````php <?php -require_once 'phpqrcode.php'; // Yeah, we're gonna need that +require_once '../phpqrcode.php'; // Yeah, we're gonna need that + +namespace RobThree\TwoFactorAuth\Providers class MyProvider implements IQRCodeProvider { public function getMimeType() { @@ -136,11 +137,8 @@ That's it. We're done! We've implemented our own provider (with help of PHP QR C ````php <?php -require_once 'src/TwoFactorAuth.php'; -require_once 'src/myprovider.php'; - -$mp = new MyProvider(); -$tfa = new TwoFactorAuth('My Company', 6, 30, 'sha1', $mp); +$mp = new RobThree\TwoFactorAuth\TwoFactorAuth\Providers\MyProvider(); +$tfa = new RobThree\TwoFactorAuth\TwoFactorAuth\TwoFactorAuth('My Company', 6, 30, 'sha1', $mp); $secret = $tfa->createSecret(); ?> <p><img src="<?php $tfa->getQRCodeImageAsDataUri('Bob Ross', $secret) ?>"></p> diff --git a/RobThree/TwoFactorAuth/Providers/BaseHTTPQRCodeProvider.php b/RobThree/TwoFactorAuth/Providers/BaseHTTPQRCodeProvider.php new file mode 100644 index 0000000..f29265d --- /dev/null +++ b/RobThree/TwoFactorAuth/Providers/BaseHTTPQRCodeProvider.php @@ -0,0 +1,29 @@ +<?php + +namespace RobThree\TwoFactorAuth\Providers; + +abstract class BaseHTTPQRCodeProvider implements IQRCodeProvider +{ + protected $verifyssl; + + protected function getContent($url) + { + $ch = curl_init(); + + curl_setopt_array($ch, array( + CURLOPT_URL => $url, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 3, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_DNS_CACHE_TIMEOUT => 10, + CURLOPT_TIMEOUT => 10, + CURLOPT_SSL_VERIFYPEER => $this->verifyssl, + CURLOPT_USERAGENT => 'TwoFactorAuth' + )); + $data = curl_exec($ch); + + curl_close($ch); + return $data; + } +}
\ No newline at end of file diff --git a/RobThree/TwoFactorAuth/Providers/GoogleQRCodeProvider.php b/RobThree/TwoFactorAuth/Providers/GoogleQRCodeProvider.php new file mode 100644 index 0000000..5084bac --- /dev/null +++ b/RobThree/TwoFactorAuth/Providers/GoogleQRCodeProvider.php @@ -0,0 +1,39 @@ +<?php + +namespace RobThree\TwoFactorAuth\Providers; + +// https://developers.google.com/chart/infographics/docs/qr_codes +class GoogleQRCodeProvider extends BaseHTTPQRCodeProvider +{ + public $errorcorrectionlevel; + public $margin; + + function __construct($verifyssl = false, $errorcorrectionlevel = 'L', $margin = 1) + { + if (!is_bool($verifyssl)) + throw new Exception('VerifySSL must be bool'); + + $this->verifyssl = $verifyssl; + + $this->errorcorrectionlevel = $errorcorrectionlevel; + $this->margin = $margin; + } + + public function getMimeType() + { + return 'image/png'; + } + + public function getQRCodeImage($qrtext, $size) + { + return $this->getContent($this->getUrl($qrtext, $size)); + } + + public function getUrl($qrtext, $size) + { + return 'https://chart.googleapis.com/chart?cht=qr' + . '&chs=' . $size . 'x' . $size + . '&chld=' . $this->errorcorrectionlevel . '|' . $this->margin + . '&chl=' . rawurlencode($qrtext); + } +}
\ No newline at end of file diff --git a/RobThree/TwoFactorAuth/Providers/IQRCodeProvider.php b/RobThree/TwoFactorAuth/Providers/IQRCodeProvider.php new file mode 100644 index 0000000..6aeb856 --- /dev/null +++ b/RobThree/TwoFactorAuth/Providers/IQRCodeProvider.php @@ -0,0 +1,9 @@ +<?php + +namespace RobThree\TwoFactorAuth\Providers; + +interface IQRCodeProvider +{ + public function getQRCodeImage($qrtext, $size); + public function getMimeType(); +}
\ No newline at end of file diff --git a/RobThree/TwoFactorAuth/Providers/QRServerProvider.php b/RobThree/TwoFactorAuth/Providers/QRServerProvider.php new file mode 100644 index 0000000..d95260a --- /dev/null +++ b/RobThree/TwoFactorAuth/Providers/QRServerProvider.php @@ -0,0 +1,70 @@ +<?php + +namespace RobThree\TwoFactorAuth\Providers; + +// http://goqr.me/api/doc/create-qr-code/ +class QRServerProvider extends BaseHTTPQRCodeProvider +{ + public $errorcorrectionlevel; + public $margin; + public $qzone; + public $bgcolor; + public $color; + public $format; + + function __construct($verifyssl = false, $errorcorrectionlevel = 'L', $margin = 4, $qzone = 1, $bgcolor = 'ffffff', $color = '000000', $format = 'png') + { + if (!is_bool($verifyssl)) + throw new Exception('VerifySSL must be bool'); + + $this->verifyssl = $verifyssl; + + $this->errorcorrectionlevel = $errorcorrectionlevel; + $this->margin = $margin; + $this->qzone = $qzone; + $this->bgcolor = $bgcolor; + $this->color = $color; + $this->format = $format; + } + + public function getMimeType() + { + switch (strtolower($this->format)) + { + case 'png': + return 'image/png'; + case 'gif': + return 'image/gif'; + case 'jpg': + case 'jpeg': + return 'image/jpeg'; + case 'svg': + return 'image/svg+xml'; + case 'eps': + return 'application/postscript'; + } + } + + public function getQRCodeImage($qrtext, $size) + { + return $this->getContent($this->getUrl($qrtext, $size)); + } + + private function decodeColor($value) + { + return vsprintf('%d-%d-%d', sscanf($value, "%02x%02x%02x")); + } + + public function getUrl($qrtext, $size) + { + return 'https://api.qrserver.com/v1/create-qr-code/' + . '?size=' . $size . 'x' . $size + . '&ecc=' . strtoupper($this->errorcorrectionlevel) + . '&margin=' . $this->margin + . '&qzone=' . $this->qzone + . '&bgcolor=' . $this->decodeColor($this->bgcolor) + . '&color=' . $this->decodeColor($this->color) + . '&format=' . strtolower($this->format) + . '&data=' . rawurlencode($qrtext); + } +}
\ No newline at end of file diff --git a/RobThree/TwoFactorAuth/Providers/QRicketProvider.php b/RobThree/TwoFactorAuth/Providers/QRicketProvider.php new file mode 100644 index 0000000..2356235 --- /dev/null +++ b/RobThree/TwoFactorAuth/Providers/QRicketProvider.php @@ -0,0 +1,53 @@ +<?php + +namespace RobThree\TwoFactorAuth\Providers; + +// http://qrickit.com/qrickit_apps/qrickit_api.php +class QRicketProvider extends BaseHTTPQRCodeProvider +{ + public $errorcorrectionlevel; + public $margin; + public $qzone; + public $bgcolor; + public $color; + public $format; + + function __construct($errorcorrectionlevel = 'L', $bgcolor = 'ffffff', $color = '000000', $format = 'p') + { + $this->verifyssl = false; + + $this->errorcorrectionlevel = $errorcorrectionlevel; + $this->bgcolor = $bgcolor; + $this->color = $color; + $this->format = $format; + } + + public function getMimeType() + { + switch (strtolower($this->format)) + { + case 'p': + return 'image/png'; + case 'g': + return 'image/gif'; + case 'j': + return 'image/jpeg'; + } + } + + public function getQRCodeImage($qrtext, $size) + { + return $this->getContent($this->getUrl($qrtext, $size)); + } + + public function getUrl($qrtext, $size) + { + return 'http://qrickit.com/api/qr' + . '?qrsize=' . $size + . '&e=' . strtolower($this->errorcorrectionlevel) + . '&bgdcolor=' . $this->bgcolor + . '&fgdcolor=' . $this->color + . '&t=' . strtolower($this->format) + . '&d=' . rawurlencode($qrtext); + } +}
\ No newline at end of file diff --git a/RobThree/TwoFactorAuth/TwoFactorAuth.php b/RobThree/TwoFactorAuth/TwoFactorAuth.php new file mode 100644 index 0000000..a825d46 --- /dev/null +++ b/RobThree/TwoFactorAuth/TwoFactorAuth.php @@ -0,0 +1,147 @@ +<?php + +namespace RobThree\TwoFactorAuth; + +// Based on / inspired by: https://github.com/PHPGangsta/GoogleAuthenticator +// Algorithms, digits, period etc. explained: https://code.google.com/p/google-authenticator/wiki/KeyUriFormat +class TwoFactorAuth +{ + private $algorithm; + private $period; + private $digits; + private $issuer; + private $qrcodeprovider; + private static $_base32; + private static $_base32lookup = array(); + private static $_supportedalgos = array('sha1', 'sha256', 'sha512', 'md5'); + + function __construct($issuer = null, $digits = 6, $period = 30, $algorithm = 'sha1', $qrcodeprovider = null) + { + $this->issuer = $issuer; + + if (!is_int($digits) || $digits <= 0) + throw new Exception('Digits must be int > 0'); + $this->digits = $digits; + + if (!is_int($period) || $period <= 0) + throw new Exception('Period must be int > 0'); + $this->period = $period; + + $algorithm = strtolower(trim($algorithm)); + if (!in_array($algorithm, self::$_supportedalgos)) + throw new Exception('Unsupported algorithm: ' . $algorithm); + $this->algorithm = $algorithm; + + if ($qrcodeprovider==null) + $qrcodeprovider = new Providers\GoogleQRCodeProvider(); + + if (!($qrcodeprovider instanceof Providers\IQRCodeProvider)) + throw new Exception('QRCodeProvider must implement IQRCodeProvider'); + + $this->qrcodeprovider = $qrcodeprovider; + + self::$_base32 = str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567='); + self::$_base32lookup = array_flip(self::$_base32); + } + + /** + * Create a new secret + */ + public function createSecret($bits = 80) + { + $secret = ''; + $bytes = ceil($bits / 5); //We use 5 bits of each byte + $rnd = openssl_random_pseudo_bytes($bytes); + for ($i = 0; $i < $bytes; $i++) + $secret .= self::$_base32[ord($rnd[$i]) & 31]; //Mask out left 3 bits for 0-31 values + return $secret; + } + + /** + * Calculate the code with given secret and point in time + */ + public function getCode($secret, $time = null) + { + $secretkey = $this->base32Decode($secret); + + $ts = "\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 + $hashpart = substr($hm, ord(substr($hm, -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 = $value[1] & 0x7FFFFFFF; // Drop MSB, keep only 31 bits + + return str_pad($value % pow(10, $this->digits), $this->digits, '0', STR_PAD_LEFT); + } + + /** + * 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) + { + $t = $this->getTime($time); + for ($i = -$discrepancy; $i <= $discrepancy; $i++) + { + if (strcmp($this->getCode($secret, $t + ($i * $this->period)), $code) === 0) + return true; + } + + return false; + } + + /** + * Get data-uri of QRCode + */ + public function getQRCodeImageAsDataUri($label, $secret, $size = 200) + { + if (!is_int($size) || $size < 0) + throw new Exception('Size must be int > 0'); + + return 'data:' + . $this->qrcodeprovider->getMimeType() + . ';base64,' + . base64_encode($this->qrcodeprovider->getQRCodeImage($this->getQRText($label, $secret), $size)); + } + + private function getTime($time) + { + return ($time === null) ? time() : $time; + } + + private function getTimeSlice($time = null, $offset = 0) + { + return (int)floor($time / $this->period) + ($offset * $this->period); + } + + /** + * Builds a string to be encoded in a QR code + */ + private function getQRText($label, $secret) + { + return 'otpauth://totp/' . rawurlencode($label) + . '?secret=' . rawurlencode($secret) + . '&issuer=' . rawurlencode($this->issuer) + . '&period=' . intval($this->period) + . '&algorithm=' . rawurlencode(strtoupper($this->algorithm)) + . '&digits=' . intval($this->digits); + } + + private function base32Decode($value) + { + if (strlen($value)==0) return ''; + + $s = ''; + foreach (str_split($value) as $c) + { + if ($c !== '=') + $s .= str_pad(decbin(self::$_base32lookup[$c]), 5, 0, STR_PAD_LEFT); + } + $l = strlen($s); + $r = trim(chunk_split(substr($s, 0, $l - ($l % 8)), 8, ' ')); + + $o = ''; + foreach (explode(' ', $r) as $b) + $o .= chr(bindec(str_pad($b, 8, 0, STR_PAD_RIGHT))); + + return $o; + } +}
\ No newline at end of file diff --git a/TwoFactorAuth.phpproj b/TwoFactorAuth.phpproj index 9d36825..ca8028e 100644 --- a/TwoFactorAuth.phpproj +++ b/TwoFactorAuth.phpproj @@ -23,12 +23,20 @@ </PropertyGroup> <ItemGroup> <Compile Include="demo.php" /> - <Compile Include="src\TwoFactorAuth.php" /> + <Compile Include="loader.php" /> + <Compile Include="RobThree\TwoFactorAuth\Providers\BaseHTTPQRCodeProvider.php" /> + <Compile Include="RobThree\TwoFactorAuth\Providers\GoogleQRCodeProvider.php" /> + <Compile Include="RobThree\TwoFactorAuth\Providers\IQRCodeProvider.php" /> + <Compile Include="RobThree\TwoFactorAuth\Providers\QRicketProvider.php" /> + <Compile Include="RobThree\TwoFactorAuth\Providers\QRServerProvider.php" /> + <Compile Include="RobThree\TwoFactorAuth\TwoFactorAuth.php" /> <Compile Include=".gitignore" /> <Compile Include="README.md" /> </ItemGroup> <ItemGroup> - <Folder Include="src\" /> + <Folder Include="RobThree\" /> + <Folder Include="RobThree\TwoFactorAuth\" /> + <Folder Include="RobThree\TwoFactorAuth\Providers\" /> </ItemGroup> <ItemGroup> <Content Include="LICENSE" /> @@ -7,9 +7,12 @@ <ol> <?php error_reporting(-1); - require_once 'src/TwoFactorAuth.php'; - - $tfa = new TwoFactorAuth('MyApp'); + require_once 'loader.php'; + Loader::register('RobThree','RobThree'); + + use RobThree\TwoFactorAuth; + + $tfa = new TwoFactorAuth\TwoFactorAuth('MyApp', 6, 30, 'sha1', new TwoFactorAuth\Providers\QRicketProvider()); echo '<li>First create a secret and associate it with a user'; $secret = $tfa->createSecret(); diff --git a/loader.php b/loader.php new file mode 100644 index 0000000..208f24d --- /dev/null +++ b/loader.php @@ -0,0 +1,50 @@ +<?php + +//http://www.leaseweblabs.com/2014/04/psr-0-psr-4-autoloading-classes-php/ +class Loader +{ + protected static $parentPath = null; + protected static $paths = null; + protected static $files = null; + protected static $nsChar = '\\'; + protected static $initialized = false; + + protected static function initialize() + { + if (static::$initialized) return; + static::$initialized = true; + static::$parentPath = __FILE__; + for ($i=substr_count(get_class(), static::$nsChar);$i>=0;$i--) { + static::$parentPath = dirname(static::$parentPath); + } + static::$paths = array(); + static::$files = array(__FILE__); + } + + public static function register($path,$namespace) { + if (!static::$initialized) static::initialize(); + static::$paths[$namespace] = trim($path,DIRECTORY_SEPARATOR); + } + + public static function load($class) { + if (class_exists($class,false)) return; + if (!static::$initialized) static::initialize(); + + foreach (static::$paths as $namespace => $path) { + if (!$namespace || $namespace.static::$nsChar === substr($class, 0, strlen($namespace.static::$nsChar))) { + + $fileName = substr($class,strlen($namespace.static::$nsChar)-1); + $fileName = str_replace(static::$nsChar, DIRECTORY_SEPARATOR, ltrim($fileName,static::$nsChar)); + $fileName = static::$parentPath.DIRECTORY_SEPARATOR.$path.DIRECTORY_SEPARATOR.$fileName.'.php'; + + if (file_exists($fileName)) { + include $fileName; + return true; + } + } + } + return false; + } +} + +spl_autoload_register(array('Loader', 'load'));
\ No newline at end of file diff --git a/src/TwoFactorAuth.php b/src/TwoFactorAuth.php deleted file mode 100644 index 6cffa6d..0000000 --- a/src/TwoFactorAuth.php +++ /dev/null @@ -1,329 +0,0 @@ -<?php -// Based on / inspired by: https://github.com/PHPGangsta/GoogleAuthenticator -// Algorithms, digits, period etc. explained: https://code.google.com/p/google-authenticator/wiki/KeyUriFormat -class TwoFactorAuth -{ - private $algorithm; - private $period; - private $digits; - private $issuer; - private $qrcodeprovider; - private static $_base32; - private static $_base32lookup = array(); - private static $_supportedalgos = array('sha1', 'sha256', 'sha512', 'md5'); - - function __construct($issuer = null, $digits = 6, $period = 30, $algorithm = 'sha1', $qrcodeprovider = null) - { - $this->issuer = $issuer; - - if (!is_int($digits) || $digits <= 0) - throw new Exception('Digits must be int > 0'); - $this->digits = $digits; - - if (!is_int($period) || $period <= 0) - throw new Exception('Period must be int > 0'); - $this->period = $period; - - $algorithm = strtolower(trim($algorithm)); - if (!in_array($algorithm, self::$_supportedalgos)) - throw new Exception('Unsupported algorithm: ' . $algorithm); - $this->algorithm = $algorithm; - - if ($qrcodeprovider==null) - $qrcodeprovider = new GoogleQRCodeProvider(); - - if (!($qrcodeprovider instanceof IQRCodeProvider)) - throw new Exception('QRCodeProvider must implement IQRCodeProvider'); - - $this->qrcodeprovider = $qrcodeprovider; - - self::$_base32 = str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567='); - self::$_base32lookup = array_flip(self::$_base32); - } - - /** - * Create a new secret - */ - public function createSecret($bits = 80) - { - $secret = ''; - $bytes = ceil($bits / 5); //We use 5 bits of each byte - $rnd = openssl_random_pseudo_bytes($bytes); - for ($i = 0; $i < $bytes; $i++) - $secret .= self::$_base32[ord($rnd[$i]) & 31]; //Mask out left 3 bits for 0-31 values - return $secret; - } - - /** - * Calculate the code with given secret and point in time - */ - public function getCode($secret, $time = null) - { - $secretkey = $this->base32Decode($secret); - - $ts = "\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 - $hashpart = substr($hm, ord(substr($hm, -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 = $value[1] & 0x7FFFFFFF; // Drop MSB, keep only 31 bits - - return str_pad($value % pow(10, $this->digits), $this->digits, '0', STR_PAD_LEFT); - } - - /** - * 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) - { - $t = $this->getTime($time); - for ($i = -$discrepancy; $i <= $discrepancy; $i++) - { - if (strcmp($this->getCode($secret, $t + ($i * $this->period)), $code) === 0) - return true; - } - - return false; - } - - /** - * Get data-uri of QRCode - */ - public function getQRCodeImageAsDataUri($label, $secret, $size = 200) - { - if (!is_int($size) || $size < 0) - throw new Exception('Size must be int > 0'); - - return 'data:' - . $this->qrcodeprovider->getMimeType() - . ';base64,' - . base64_encode($this->qrcodeprovider->getQRCodeImage($this->getQRText($label, $secret), $size)); - } - - private function getTime($time) - { - return ($time === null) ? time() : $time; - } - - private function getTimeSlice($time = null, $offset = 0) - { - return (int)floor($time / $this->period) + ($offset * $this->period); - } - - /** - * Builds a string to be encoded in a QR code - */ - private function getQRText($label, $secret) - { - return 'otpauth://totp/' . rawurlencode($label) - . '?secret=' . rawurlencode($secret) - . '&issuer=' . rawurlencode($this->issuer) - . '&period=' . intval($this->period) - . '&algorithm=' . rawurlencode(strtoupper($this->algorithm)) - . '&digits=' . intval($this->digits); - } - - private function base32Decode($value) - { - if (strlen($value)==0) return ''; - - $s = ''; - foreach (str_split($value) as $c) - { - if ($c !== '=') - $s .= str_pad(decbin(self::$_base32lookup[$c]), 5, 0, STR_PAD_LEFT); - } - $l = strlen($s); - $r = trim(chunk_split(substr($s, 0, $l - ($l % 8)), 8, ' ')); - - $o = ''; - foreach (explode(' ', $r) as $b) - $o .= chr(bindec(str_pad($b, 8, 0, STR_PAD_RIGHT))); - - return $o; - } -} - -interface IQRCodeProvider -{ - public function getQRCodeImage($qrtext, $size); - public function getMimeType(); -} - -abstract class BaseHTTPQRCodeProvider implements IQRCodeProvider -{ - protected $verifyssl; - - protected function getContent($url) - { - $ch = curl_init(); - - curl_setopt_array($ch, array( - CURLOPT_URL => $url, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_MAXREDIRS => 3, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_CONNECTTIMEOUT => 10, - CURLOPT_DNS_CACHE_TIMEOUT => 10, - CURLOPT_TIMEOUT => 10, - CURLOPT_SSL_VERIFYPEER => $this->verifyssl, - CURLOPT_USERAGENT => 'TwoFactorAuth' - )); - $data = curl_exec($ch); - - curl_close($ch); - return $data; - } -} - -// https://developers.google.com/chart/infographics/docs/qr_codes -class GoogleQRCodeProvider extends BaseHTTPQRCodeProvider -{ - public $errorcorrectionlevel; - public $margin; - - function __construct($verifyssl = false, $errorcorrectionlevel = 'L', $margin = 1) - { - if (!is_bool($verifyssl)) - throw new Exception('VerifySSL must be bool'); - - $this->verifyssl = $verifyssl; - - $this->errorcorrectionlevel = $errorcorrectionlevel; - $this->margin = $margin; - } - - public function getMimeType() - { - return 'image/png'; - } - - public function getQRCodeImage($qrtext, $size) - { - return $this->getContent($this->getUrl($qrtext, $size)); - } - - public function getUrl($qrtext, $size) - { - return 'https://chart.googleapis.com/chart?cht=qr' - . '&chs=' . $size . 'x' . $size - . '&chld=' . $this->errorcorrectionlevel . '|' . $this->margin - . '&chl=' . rawurlencode($qrtext); - } -} - -// http://goqr.me/api/doc/create-qr-code/ -class QRServerProvider extends BaseHTTPQRCodeProvider -{ - public $errorcorrectionlevel; - public $margin; - public $qzone; - public $bgcolor; - public $color; - public $format; - - function __construct($verifyssl = false, $errorcorrectionlevel = 'L', $margin = 4, $qzone = 1, $bgcolor = 'ffffff', $color = '000000', $format = 'png') - { - if (!is_bool($verifyssl)) - throw new Exception('VerifySSL must be bool'); - - $this->verifyssl = $verifyssl; - - $this->errorcorrectionlevel = $errorcorrectionlevel; - $this->margin = $margin; - $this->qzone = $qzone; - $this->bgcolor = $bgcolor; - $this->color = $color; - $this->format = $format; - } - - public function getMimeType() - { - switch (strtolower($this->format)) - { - case 'png': - return 'image/png'; - case 'gif': - return 'image/gif'; - case 'jpg': - case 'jpeg': - return 'image/jpeg'; - case 'svg': - return 'image/svg+xml'; - case 'eps': - return 'application/postscript'; - } - } - - public function getQRCodeImage($qrtext, $size) - { - return $this->getContent($this->getUrl($qrtext, $size)); - } - - private function decodeColor($value) - { - return vsprintf('%d-%d-%d', sscanf($value, "%02x%02x%02x")); - } - - public function getUrl($qrtext, $size) - { - return 'https://api.qrserver.com/v1/create-qr-code/' - . '?size=' . $size . 'x' . $size - . '&ecc=' . strtoupper($this->errorcorrectionlevel) - . '&margin=' . $this->margin - . '&qzone=' . $this->qzone - . '&bgcolor=' . $this->decodeColor($this->bgcolor) - . '&color=' . $this->decodeColor($this->color) - . '&format=' . strtolower($this->format) - . '&data=' . rawurlencode($qrtext); - } -} - -// http://qrickit.com/qrickit_apps/qrickit_api.php -class QRicketProvider extends BaseHTTPQRCodeProvider -{ - public $errorcorrectionlevel; - public $margin; - public $qzone; - public $bgcolor; - public $color; - public $format; - - function __construct($errorcorrectionlevel = 'L', $bgcolor = 'ffffff', $color = '000000', $format = 'p') - { - $this->verifyssl = false; - - $this->errorcorrectionlevel = $errorcorrectionlevel; - $this->bgcolor = $bgcolor; - $this->color = $color; - $this->format = $format; - } - - public function getMimeType() - { - switch (strtolower($this->format)) - { - case 'p': - return 'image/png'; - case 'g': - return 'image/gif'; - case 'j': - return 'image/jpeg'; - } - } - - public function getQRCodeImage($qrtext, $size) - { - return $this->getContent($this->getUrl($qrtext, $size)); - } - - public function getUrl($qrtext, $size) - { - return 'http://qrickit.com/api/qr' - . '?qrsize=' . $size - . '&e=' . strtolower($this->errorcorrectionlevel) - . '&bgdcolor=' . $this->bgcolor - . '&fgdcolor=' . $this->color - . '&t=' . strtolower($this->format) - . '&d=' . rawurlencode($qrtext); - } -}
\ No newline at end of file |