summaryrefslogtreecommitdiff
path: root/vendor/christian-riesen/otp/src/Otp
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/christian-riesen/otp/src/Otp')
-rw-r--r--vendor/christian-riesen/otp/src/Otp/GoogleAuthenticator.php189
-rw-r--r--vendor/christian-riesen/otp/src/Otp/Otp.php310
-rw-r--r--vendor/christian-riesen/otp/src/Otp/OtpInterface.php65
3 files changed, 564 insertions, 0 deletions
diff --git a/vendor/christian-riesen/otp/src/Otp/GoogleAuthenticator.php b/vendor/christian-riesen/otp/src/Otp/GoogleAuthenticator.php
new file mode 100644
index 00000000..23e67ff0
--- /dev/null
+++ b/vendor/christian-riesen/otp/src/Otp/GoogleAuthenticator.php
@@ -0,0 +1,189 @@
+<?php
+namespace Otp;
+
+/**
+ * Google Authenticator
+ *
+ * Last update: 2014-08-19
+ *
+ * Can be easy used with Google Authenticator
+ * @link https://code.google.com/p/google-authenticator/
+ *
+ * @author Christian Riesen <chris.riesen@gmail.com>
+ * @link http://christianriesen.com
+ * @license MIT License see LICENSE file
+ */
+
+class GoogleAuthenticator
+{
+ protected static $allowedTypes = array('hotp', 'totp');
+
+ protected static $height = 200;
+ protected static $width = 200;
+
+ /**
+ * Returns the Key URI
+ *
+ * Format of encoded url is here:
+ * https://code.google.com/p/google-authenticator/wiki/KeyUriFormat
+ * Should be done in a better fashion
+ *
+ * @param string $type totp or hotp
+ * @param string $label Label to display this as to the user
+ * @param string $secret Base32 encoded secret
+ * @param integer $counter Required by hotp, otherwise ignored
+ * @param array $options Optional fields that will be set if present
+ *
+ * @return string Key URI
+ */
+ public static function getKeyUri($type, $label, $secret, $counter = null, $options = array())
+ {
+ // two types only..
+ if (!in_array($type, self::$allowedTypes)) {
+ throw new \InvalidArgumentException('Type has to be of allowed types list');
+ }
+
+ // Label can't be empty
+ $label = trim($label);
+
+ if (strlen($label) < 1) {
+ throw new \InvalidArgumentException('Label has to be one or more printable characters');
+ }
+
+ if (substr_count($label, ':') > 2) {
+ throw new \InvalidArgumentException('Account name contains illegal colon characters');
+ }
+
+ // Secret needs to be here
+ if (strlen($secret) < 1) {
+ throw new \InvalidArgumentException('No secret present');
+ }
+
+ // check for counter on hotp
+ if ($type == 'hotp' && is_null($counter)) {
+ throw new \InvalidArgumentException('Counter required for hotp');
+ }
+
+ // This is the base, these are at least required
+ $otpauth = 'otpauth://' . $type . '/' . str_replace(array(':', ' '), array('%3A', '%20'), $label) . '?secret=' . rawurlencode($secret);
+
+ if ($type == 'hotp' && !is_null($counter)) {
+ $otpauth .= '&counter=' . intval($counter);
+ }
+
+ // Now check the options array
+
+ // algorithm (currently ignored by Authenticator)
+ // Defaults to SHA1
+ if (array_key_exists('algorithm', $options)) {
+ $otpauth .= '&algorithm=' . rawurlencode($options['algorithm']);
+ }
+
+ // digits (currently ignored by Authenticator)
+ // Defaults to 6
+ if (array_key_exists('digits', $options) && intval($options['digits']) !== 6 && intval($options['digits']) !== 8) {
+ throw new \InvalidArgumentException('Digits can only have the values 6 or 8, ' . $options['digits'] . ' given');
+ } elseif (array_key_exists('digits', $options)) {
+ $otpauth .= '&digits=' . intval($options['digits']);
+ }
+
+ // period, only for totp (currently ignored by Authenticator)
+ // Defaults to 30
+ if ($type == 'totp' && array_key_exists('period', $options)) {
+ $otpauth .= '&period=' . rawurlencode($options['period']);
+ }
+
+ // issuer
+ // Defaults to none
+ if (array_key_exists('issuer', $options)) {
+ $otpauth .= '&issuer=' . rawurlencode($options['issuer']);
+ }
+
+ return $otpauth;
+ }
+
+
+ /**
+ * Returns the QR code url
+ *
+ * Format of encoded url is here:
+ * https://code.google.com/p/google-authenticator/wiki/KeyUriFormat
+ * Should be done in a better fashion
+ *
+ * @param string $type totp or hotp
+ * @param string $label Label to display this as to the user
+ * @param string $secret Base32 encoded secret
+ * @param integer $counter Required by hotp, otherwise ignored
+ * @param array $options Optional fields that will be set if present
+ *
+ * @return string URL to the QR code
+ */
+ public static function getQrCodeUrl($type, $label, $secret, $counter = null, $options = array())
+ {
+ // Width and height can be overwritten
+ $width = self::$width;
+
+ if (array_key_exists('width', $options) && is_numeric($options['width'])) {
+ $width = $options['width'];
+ }
+
+ $height = self::$height;
+
+ if (array_key_exists('height', $options) && is_numeric($options['height'])) {
+ $height = $options['height'];
+ }
+
+ $otpauth = self::getKeyUri($type, $label, $secret, $counter, $options);
+
+ $url = 'https://chart.googleapis.com/chart?chs=' . $width . 'x'
+ . $height . '&cht=qr&chld=M|0&chl=' . urlencode($otpauth);
+
+ return $url;
+ }
+
+ /**
+ * Creates a pseudo random Base32 string
+ *
+ * This could decode into anything. It's located here as a small helper
+ * where code that might need base32 usually also needs something like this.
+ *
+ * @param integer $length Exact length of output string
+ * @return string Base32 encoded random
+ */
+ public static function generateRandom($length = 16)
+ {
+ $keys = array_merge(range('A','Z'), range(2,7)); // No padding char
+
+ $string = '';
+
+ for ($i = 0; $i < $length; $i++) {
+ $string .= $keys[self::getRand()];
+ }
+
+ return $string;
+ }
+
+ /**
+ * Get random number
+ *
+ * @return int Random number between 0 and 31 (including)
+ */
+ private static function getRand()
+ {
+ if (function_exists('random_int')) {
+ // Uses either the PHP7 internal function or the polyfill if present
+ return random_int(0, 31);
+ } elseif (function_exists('openssl_random_pseudo_bytes')) {
+ $bytes = openssl_random_pseudo_bytes(2);
+ $number = hexdec(bin2hex($bytes));
+
+ if ($number > 31) {
+ $number = $number % 32;
+ }
+
+ return $number;
+ } else {
+ return mt_rand(0, 31);
+ }
+ }
+}
diff --git a/vendor/christian-riesen/otp/src/Otp/Otp.php b/vendor/christian-riesen/otp/src/Otp/Otp.php
new file mode 100644
index 00000000..7d954870
--- /dev/null
+++ b/vendor/christian-riesen/otp/src/Otp/Otp.php
@@ -0,0 +1,310 @@
+<?php
+namespace Otp;
+
+/**
+ * One Time Passwords
+ *
+ * Last update: 2012-06-16
+ *
+ * Implements HOTP and TOTP
+ *
+ * HMAC-Based One-time Password(HOTP) algorithm specified in RFC 4226
+ * @link https://tools.ietf.org/html/rfc4226
+ *
+ * Time-based One-time Password (TOTP) algorithm specified in RFC 6238
+ * @link https://tools.ietf.org/html/rfc6238
+ *
+ * As a note: This code is NOT 2038 proof! The min concern is the function
+ * getBinaryCounter that uses the pack function which can't handle 64bit yet.
+ *
+ * Can be easy used with Google Authenticator
+ * @link https://code.google.com/p/google-authenticator/
+ *
+ * @author Christian Riesen <chris.riesen@gmail.com>
+ * @link http://christianriesen.com
+ * @license MIT License see LICENSE file
+ */
+
+class Otp implements OtpInterface
+{
+ /**
+ * The digits the code can have
+ *
+ * Either 6 or 8.
+ * Authenticator does only support 6.
+ *
+ * @var integer
+ */
+ protected $digits = 6;
+
+ /**
+ * Time in seconds one counter period is long
+ *
+ * @var integer
+ */
+ protected $period = 30;
+
+ /**
+ * Possible algorithms
+ *
+ * @var array
+ */
+ protected $allowedAlgorithms = array('sha1', 'sha256', 'sha512');
+
+ /**
+ * Currently used algorithm
+ *
+ * @var string
+ */
+ protected $algorithm = 'sha1';
+
+ /* (non-PHPdoc)
+ * @see Otp.OtpInterface::hotp()
+ */
+ public function hotp($secret, $counter)
+ {
+ if (!is_numeric($counter)) {
+ throw new \InvalidArgumentException('Counter must be integer');
+ }
+
+ $hash = hash_hmac(
+ $this->algorithm,
+ $this->getBinaryCounter($counter),
+ $secret,
+ true
+ );
+
+ return str_pad($this->truncate($hash), $this->digits, '0', STR_PAD_LEFT);
+ }
+
+ /* (non-PHPdoc)
+ * @see Otp.OtpInterface::totp()
+ */
+ public function totp($secret, $timecounter = null)
+ {
+ if (is_null($timecounter)) {
+ $timecounter = $this->getTimecounter();
+ }
+
+ return $this->hotp($secret, $timecounter);
+ }
+
+ /* (non-PHPdoc)
+ * @see Otp.OtpInterface::checkHotp()
+ */
+ public function checkHotp($secret, $counter, $key)
+ {
+ return $this->safeCompare($this->hotp($secret, $counter), $key);
+ }
+
+ /* (non-PHPdoc)
+ * @see Otp.OtpInterface::checkTotp()
+ */
+ public function checkTotp($secret, $key, $timedrift = 1)
+ {
+ if (!is_numeric($timedrift) || $timedrift < 0) {
+ throw new \InvalidArgumentException('Invalid timedrift supplied');
+ }
+ // Counter comes from time now
+ // Also we check the current timestamp as well as previous and future ones
+ // according to $timerange
+ $timecounter = $this->getTimecounter();
+
+ $start = $timecounter - ($timedrift);
+ $end = $timecounter + ($timedrift);
+
+ // We first try the current, as it is the most likely to work
+ if ($this->safeCompare($this->totp($secret, $timecounter), $key)) {
+ return true;
+ } elseif ($timedrift == 0) {
+ // When timedrift is 0, this is the end of the checks
+ return false;
+ }
+
+ // Well, that didn't work, so try the others
+ for ($t = $start; $t <= $end; $t = $t + 1) {
+ if ($t == $timecounter) {
+ // Already tried that one
+ continue;
+ }
+
+ if ($this->safeCompare($this->totp($secret, $t), $key)) {
+ return true;
+ }
+ }
+
+ // if none worked, then return false
+ return false;
+ }
+
+ /**
+ * Changing the used algorithm for hashing
+ *
+ * Can only be one of the algorithms in the allowedAlgorithms property.
+ *
+ * @param string $algorithm
+ * @throws \InvalidArgumentException
+ * @return \Otp\Otp
+ */
+
+ /*
+ * This has been disabled since it does not bring the expected results
+ * according to the RFC test vectors for sha256 or sha512.
+ * Until that is fixed, the algorithm simply stays at sha1.
+ * Google Authenticator does not support sha256 and sha512 at the moment.
+ *
+
+ public function setAlgorithm($algorithm)
+ {
+ if (!in_array($algorithm, $this->allowedAlgorithms)) {
+ throw new \InvalidArgumentException('Not an allowed algorithm: ' . $algorithm);
+ }
+
+ $this->algorithm = $algorithm;
+
+ return $this;
+ }
+ // */
+
+ /**
+ * Get the algorithms name (lowercase)
+ *
+ * @return string
+ */
+ public function getAlgorithm()
+ {
+ return $this->algorithm;
+ }
+
+ /**
+ * Setting period lenght for totp
+ *
+ * @param integer $period
+ * @throws \InvalidArgumentException
+ * @return \Otp\Otp
+ */
+ public function setPeriod($period)
+ {
+ if (!is_int($period)) {
+ throw new \InvalidArgumentException('Period must be an integer');
+ }
+
+ $this->period = $period;
+
+ return $this;
+ }
+
+ /**
+ * Returns the set period value
+ *
+ * @return integer
+ */
+ public function getPeriod()
+ {
+ return $this->period;
+ }
+
+ /**
+ * Setting number of otp digits
+ *
+ * @param integer $digits Number of digits for the otp (6 or 8)
+ * @throws \InvalidArgumentException
+ * @return \Otp\Otp
+ */
+ public function setDigits($digits)
+ {
+ if (!in_array($digits, array(6, 8))) {
+ throw new \InvalidArgumentException('Digits must be 6 or 8');
+ }
+
+ $this->digits = $digits;
+
+ return $this;
+ }
+
+ /**
+ * Returns number of digits in the otp
+ *
+ * @return integer
+ */
+ public function getDigits()
+ {
+ return $this->digits;
+ }
+
+ /**
+ * Generates a binary counter for hashing
+ *
+ * Warning: Not 2038 safe. Maybe until then pack supports 64bit.
+ *
+ * @param integer $counter Counter in integer form
+ * @return string Binary string
+ */
+ protected function getBinaryCounter($counter)
+ {
+ return pack('N*', 0) . pack('N*', $counter);
+ }
+
+ /**
+ * Generating time counter
+ *
+ * This is the time divided by 30 by default.
+ *
+ * @return integer Time counter
+ */
+ protected function getTimecounter()
+ {
+ return floor(time() / $this->period);
+ }
+
+ /**
+ * Creates the basic number for otp from hash
+ *
+ * This number is left padded with zeros to the required length by the
+ * calling function.
+ *
+ * @param string $hash hmac hash
+ * @return number
+ */
+ protected function truncate($hash)
+ {
+ $offset = ord($hash[19]) & 0xf;
+
+ return (
+ ((ord($hash[$offset+0]) & 0x7f) << 24 ) |
+ ((ord($hash[$offset+1]) & 0xff) << 16 ) |
+ ((ord($hash[$offset+2]) & 0xff) << 8 ) |
+ (ord($hash[$offset+3]) & 0xff)
+ ) % pow(10, $this->digits);
+ }
+
+ /**
+ * Safely compares two inputs
+ *
+ * Assumed inputs are numbers and strings.
+ * Compares them in a time linear manner. No matter how much you guess
+ * correct of the partial content, it does not change the time it takes to
+ * run the entire comparison.
+ *
+ * @param mixed $a
+ * @param mixed $b
+ * @return boolean
+ */
+ protected function safeCompare($a, $b)
+ {
+ $sha1a = sha1($a);
+ $sha1b = sha1($b);
+
+ // Now the compare is always the same length. Even considering minute
+ // time differences in sha1 creation, all you know is that a longer
+ // input takes longer to hash, not how long the actual compared value is
+ $result = 0;
+
+ for ($i = 0; $i < 40; $i++) {
+ $result |= ord($sha1a[$i]) ^ ord($sha1b[$i]);
+ }
+
+ return $result == 0;
+ }
+}
+
diff --git a/vendor/christian-riesen/otp/src/Otp/OtpInterface.php b/vendor/christian-riesen/otp/src/Otp/OtpInterface.php
new file mode 100644
index 00000000..7ff34f2f
--- /dev/null
+++ b/vendor/christian-riesen/otp/src/Otp/OtpInterface.php
@@ -0,0 +1,65 @@
+<?php
+namespace Otp;
+
+/**
+ * Interface for HOTP and TOTP
+ *
+ * Last update: 2012-06-16
+ *
+ * HMAC-Based One-time Password(HOTP) algorithm specified in RFC 4226
+ * @link https://tools.ietf.org/html/rfc4226
+ *
+ * Time-based One-time Password (TOTP) algorithm specified in RFC 6238
+ * @link https://tools.ietf.org/html/rfc6238
+ *
+ * @author Christian Riesen <chris.riesen@gmail.com>
+ * @link http://christianriesen.com
+ * @license MIT License see LICENSE file
+ */
+
+interface OtpInterface
+{
+ /**
+ * Returns OTP using the HOTP algorithm
+ *
+ * @param string $secret
+ * @param integer $counter
+ * @return string One Time Password
+ */
+ function hotp($secret, $counter);
+
+ /**
+ * Returns OTP using the TOTP algorithm
+ *
+ * @param string $secret
+ * @param integer $timecounter Optional: Uses current time if null
+ * @return string One Time Password
+ */
+ function totp($secret, $timecounter = null);
+
+ /**
+ * Checks Hotp against a key
+ *
+ * This is a helper function, but is here to ensure the Totp can be checked
+ * in the same manner.
+ *
+ * @param string $secret
+ * @param integer $counter
+ * @param string $key
+ *
+ * @return boolean If key is correct
+ */
+ function checkHotp($secret, $counter, $key);
+
+ /**
+ * Checks Totp agains a key
+ *
+ *
+ * @param string $secret
+ * @param integer $key
+ * @param integer $timedrift
+ *
+ * @return boolean If key is correct
+ */
+ function checkTotp($secret, $key, $timedrift = 1);
+}