Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/security/mfa/ |
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/security/mfa/otpalgorithm.php |
<?php namespace Bitrix\Main\Security\Mfa; use Bitrix\Main\ArgumentTypeException; use Bitrix\Main\Security\OtpException; use Bitrix\Main\Text\Base32; abstract class OtpAlgorithm { protected static $type = 'undefined'; protected $digest = 'sha1'; protected $digits = 6; protected $secret = null; protected $appScheme = 'otpauth'; protected $requireTwoCode = true; /** * Verify provided input * * @param string $input Input received from user. * @param string $params Synchronized user params, saved for this algorithm (see getSyncParameters). * @return array [ * bool isSuccess (Valid input or not), * string newParams (Updated user params for this OtpAlgorithm) * ] */ abstract public function verify($input, $params = null); /** * Return synchronized user params for provided inputs * * @param string $inputA First code. * @param string|null $inputB Second code. Must be provided if current OtpAlgorithm required it (see isTwoCodeRequired). * @return string * @throws OtpException */ abstract public function getSyncParameters($inputA, $inputB); /** * Require or not _two_ code for synchronize parameters * * @return bool */ public function isTwoCodeRequired() { return $this->requireTwoCode; } /** * Set new secret * * @param string $secret Secret (binary). * @return $this */ public function setSecret($secret) { $this->secret = $secret; // Backward compatibility. Use sha256 for eToken with 256bits key if (strlen($this->secret) > 25) { $this->digest = 'sha256'; } return $this; } /** * Return used secret (binary) * * @return string */ public function getSecret() { return $this->secret; } /** * Generate provision URI according to KeyUriFormat * * @link https://code.google.com/p/google-authenticator/wiki/KeyUriFormat * @param string $label User label. * @param array $opts Additional URI parameters, e.g. ['image' => 'http://example.com/my_logo.png'] . * @throws ArgumentTypeException * @return string */ public function generateUri($label, array $opts = []) { $positionalOpts = [ // Don't change order! 'secret' => Base32::encode($this->getSecret()), ]; $opts['algorithm'] = $this->getDigest(); // Digest must be in upper case for some OTP apps (e.g. Google Authenticator for iOS) $opts['algorithm'] = mb_strtoupper($opts['algorithm']); $opts['digits'] = $this->getDigits(); ksort($opts); // Some devices require a specific order for some parameters (e.g. Microsoft Authenticator require "secret" at first place %) ) $opts = array_merge( $positionalOpts, $opts ); $params = http_build_query($opts, '', '&', PHP_QUERY_RFC3986); return sprintf( '%s://%s/%s?%s', $this->getAppScheme(), $this->getType(), rawurlencode($label), $params ); } /** * Main method, generate OTP value for provided counter * * @param string|int $counter Counter. * @return string */ public function generateOTP($counter) { $hash = hash_hmac($this->getDigest(), static::toByte($counter), $this->getSecret()); $hmac = []; foreach (str_split($hash, 2) as $hex) { $hmac[] = hexdec($hex); } $offset = $hmac[count($hmac) - 1] & 0xf; $code = ($hmac[$offset] & 0x7F) << 24; $code |= ($hmac[$offset + 1] & 0xFF) << 16; $code |= ($hmac[$offset + 2] & 0xFF) << 8; $code |= ($hmac[$offset + 3] & 0xFF); $otp = $code % pow(10, $this->getDigits()); return str_pad($otp, $this->getDigits(), '0', STR_PAD_LEFT); } /** * Convert value to byte string with padding * * @param string|int $value Value for convert. Must be unsigned integer, e.g. 123, '123', '0x7b', etc. * @return string */ protected static function toByte($value) { $result = []; while ($value > 0) { $result[] = chr($value & 0xFF); $value >>= 8; } return str_pad(implode(array_reverse($result)), 8, "\000", STR_PAD_LEFT); } /** * A timing safe comparison method * * @param string $expected Expected string (e.g. input from user). * @param string $actual Actual string (e.g. generated password). * @return bool * @throws ArgumentTypeException */ protected function isStringsEqual($expected, $actual) { if (!is_string($expected)) { throw new ArgumentTypeException('expected', 'string'); } if (!is_string($actual)) { throw new ArgumentTypeException('actual', 'string'); } $lenExpected = strlen($expected); $lenActual = strlen($actual); $status = $lenExpected ^ $lenActual; $len = min($lenExpected, $lenActual); for ($i = 0; $i < $len; $i++) { $status |= ord($expected[$i]) ^ ord($actual[$i]); } return $status === 0; } /** * Returns digest algorithm used to calculate the OTP. * Mostly used for generate provision URI * * @return string */ public function getDigest() { return $this->digest; } /** * Return digits (password length) * * @return int */ public function getDigits() { return $this->digits; } /** * Return OtpAlgorithm type * * @return string */ public function getType() { return static::$type; } /** * Return algorithm scheme * Mostly used for generate provision URI * * @return string */ public function getAppScheme() { return $this->appScheme; } }