OTA PART 1
This commit is contained in:
		@@ -29,9 +29,9 @@ class UserManager
 | 
			
		||||
						setcookie ("rememberMe", $this->setEncryptedCookie($user['username']), time () + (30 * 24 * 60 * 60 * 1000), BASEDIR, $_SERVER['HTTP_HOST'], 1);
 | 
			
		||||
					}
 | 
			
		||||
					$_SESSION['user']['id'] = $user['user_id'];
 | 
			
		||||
					$page = "./index.php";
 | 
			
		||||
					$page = "home";
 | 
			
		||||
					if ($user["startPage"] == 1) {
 | 
			
		||||
						$page = "./dashboard.php";
 | 
			
		||||
						$page = "dashboard";
 | 
			
		||||
					}
 | 
			
		||||
					unset($_POST['login']);
 | 
			
		||||
					return $page;
 | 
			
		||||
@@ -101,12 +101,14 @@ class UserManager
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static function getUserData ($type) {
 | 
			
		||||
	public static function getUserData ($type, $userId = '') {
 | 
			
		||||
		if (isset($_SESSION['user']['id'])) {
 | 
			
		||||
			$user = Db::loadOne ('SELECT ' . $type . ' FROM users WHERE user_id=?', array ($_SESSION['user']['id']));
 | 
			
		||||
			return $user[$type];
 | 
			
		||||
			$userId = $_SESSION['user']['id'];
 | 
			
		||||
		} else {
 | 
			
		||||
			return "";
 | 
			
		||||
		}
 | 
			
		||||
		return "";
 | 
			
		||||
		$user = Db::loadOne ('SELECT ' . $type . ' FROM users WHERE user_id=?', array ($userId));
 | 
			
		||||
		return $user[$type];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public function setUserData ($type, $value) {
 | 
			
		||||
@@ -121,94 +123,54 @@ class UserManager
 | 
			
		||||
		return $hashPassword;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public function ulozitObrazek ($file, $path = "", $name = "") {
 | 
			
		||||
		if (!@is_array (getimagesize($file['tmp_name']))) {
 | 
			
		||||
			throw new ChybaUzivatele("Formát obrázku ". $file['name'] ." není podporován!");
 | 
			
		||||
		} else {
 | 
			
		||||
			$extension = strtolower(strrchr($file['name'], '.'));
 | 
			
		||||
			switch ($extension) {
 | 
			
		||||
				case '.jpg':
 | 
			
		||||
				case '.jpeg':
 | 
			
		||||
				$img = @imagecreatefromjpeg($file['tmp_name']);
 | 
			
		||||
				break;
 | 
			
		||||
				case '.gif':
 | 
			
		||||
					$img = @imagecreatefromgif($file['tmp_name']);
 | 
			
		||||
					break;
 | 
			
		||||
					case '.png':
 | 
			
		||||
					$img2 = @imagecreatefrompng($file['tmp_name']);
 | 
			
		||||
					break;
 | 
			
		||||
					case '.ico':
 | 
			
		||||
					$img3 = @$file['tmp_name'];
 | 
			
		||||
					break;
 | 
			
		||||
					default:
 | 
			
		||||
					$img = false;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				if($name == ""){
 | 
			
		||||
					$nazev = substr($file['name'], 0, strpos($file['name'], ".")) ."_". round(microtime(true) * 1000);
 | 
			
		||||
				}else{
 | 
			
		||||
					$nazev = $name;
 | 
			
		||||
				}
 | 
			
		||||
				if(!file_exists($path)){
 | 
			
		||||
					mkdir($path, 0777, true);
 | 
			
		||||
				}
 | 
			
		||||
				if (@$img) {
 | 
			
		||||
					if (!imagejpeg ($img, $path . $nazev .".jpg", 95)) {
 | 
			
		||||
						throw new ChybaUzivatele ("Obrázek neuložen!");
 | 
			
		||||
					}
 | 
			
		||||
					imagedestroy ($img);
 | 
			
		||||
				} else if (@$img2) {
 | 
			
		||||
					if (!imagepng ($img2, $path . $nazev .".jpg")) {
 | 
			
		||||
						throw new ChybaUzivatele ("Obrázek neuložen!");
 | 
			
		||||
					}
 | 
			
		||||
					imagedestroy ($img2);
 | 
			
		||||
				} else if (@$img3) {
 | 
			
		||||
					if (!copy($img3, $path . $nazev .'.ico')) {
 | 
			
		||||
						throw new ChybaUzivatele ("Obrázek neuložen!");
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				return array('success' => true, 'url' => $path . $nazev .".jpg");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public function atHome($userId, $atHome){
 | 
			
		||||
			try {
 | 
			
		||||
				Db::edit ('users', ['at_home' => $atHome], 'WHERE user_id = ?', array($userId));
 | 
			
		||||
			} catch(PDOException $error) {
 | 
			
		||||
				echo $error->getMessage();
 | 
			
		||||
				die();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public function changePassword($oldPassword, $newPassword, $newPassword2){
 | 
			
		||||
			if ($newPassword == $newPassword2) {
 | 
			
		||||
				//Password Criteria
 | 
			
		||||
				$oldPasswordSaved = self::getUserData('password');
 | 
			
		||||
				if (self::getHashPassword($oldPassword) == $oldPasswordSaved) {
 | 
			
		||||
					self::setUserData('password', self::getHashPassword($newPassword));
 | 
			
		||||
				} else {
 | 
			
		||||
					throw new Exception ("old password did not match");
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				throw new Exception ("new password arent same");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public function createUser($userName, $password){
 | 
			
		||||
			$userId = Db::loadOne('SELECT * FROM users WHERE username = ?;', array($userName))['user_id'];
 | 
			
		||||
			if ($userId != null) {
 | 
			
		||||
				return false;
 | 
			
		||||
			};
 | 
			
		||||
			try {
 | 
			
		||||
				$user = [
 | 
			
		||||
					'username' => $userName,
 | 
			
		||||
					'password' => self::getHashPassword($password),
 | 
			
		||||
				];
 | 
			
		||||
				return Db::add ('users', $user);
 | 
			
		||||
			} catch(PDOException $error) {
 | 
			
		||||
				echo $error->getMessage();
 | 
			
		||||
				die();
 | 
			
		||||
			}
 | 
			
		||||
	public function atHome($userId, $atHome){
 | 
			
		||||
		try {
 | 
			
		||||
			Db::edit ('users', ['at_home' => $atHome], 'WHERE user_id = ?', array($userId));
 | 
			
		||||
		} catch(PDOException $error) {
 | 
			
		||||
			echo $error->getMessage();
 | 
			
		||||
			die();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	?>
 | 
			
		||||
 | 
			
		||||
	public function changePassword($oldPassword, $newPassword, $newPassword2){
 | 
			
		||||
		if ($newPassword == $newPassword2) {
 | 
			
		||||
			//Password Criteria
 | 
			
		||||
			$oldPasswordSaved = self::getUserData('password');
 | 
			
		||||
			if (self::getHashPassword($oldPassword) == $oldPasswordSaved) {
 | 
			
		||||
				self::setUserData('password', self::getHashPassword($newPassword));
 | 
			
		||||
			} else {
 | 
			
		||||
				throw new Exception ("old password did not match");
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			throw new Exception ("new password arent same");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public function createUser($userName, $password){
 | 
			
		||||
		$userId = Db::loadOne('SELECT * FROM users WHERE username = ?;', array($userName))['user_id'];
 | 
			
		||||
		if ($userId != null) {
 | 
			
		||||
			return false;
 | 
			
		||||
		};
 | 
			
		||||
		try {
 | 
			
		||||
			$user = [
 | 
			
		||||
				'username' => $userName,
 | 
			
		||||
				'password' => self::getHashPassword($password),
 | 
			
		||||
			];
 | 
			
		||||
			return Db::add ('users', $user);
 | 
			
		||||
		} catch(PDOException $error) {
 | 
			
		||||
			echo $error->getMessage();
 | 
			
		||||
			die();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public function	haveOtaEnabled($userName){
 | 
			
		||||
		$ota = $this->getUser($userName)['ota'];
 | 
			
		||||
 | 
			
		||||
		if ($ota != ''){
 | 
			
		||||
			return ($ota != '' ? $ota : false);
 | 
			
		||||
		} else {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
?>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										43
									
								
								app/controls/login.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								app/controls/login.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
<?php
 | 
			
		||||
global $userManager;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if (
 | 
			
		||||
	isset($_POST['username']) &&
 | 
			
		||||
	$_POST['username'] != '' &&
 | 
			
		||||
	isset($_POST['password']) &&
 | 
			
		||||
	$_POST['password'] != ''
 | 
			
		||||
){
 | 
			
		||||
	$ota = false;
 | 
			
		||||
	$userName = $_POST['username'];
 | 
			
		||||
	$userPassword = $_POST['password'];
 | 
			
		||||
	$ota = $userManager->haveOtaEnabled($userName);
 | 
			
		||||
 | 
			
		||||
	$_SESSION['USERNAME'] = $userName;
 | 
			
		||||
	$_SESSION['PASSWORD'] = $userPassword;
 | 
			
		||||
	$_SESSION['OTA'] = $ota;
 | 
			
		||||
} else if (
 | 
			
		||||
	isset($_POST['otaCode']) &&
 | 
			
		||||
	$_POST['otaCode'] != ''
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
	$otaCode = $_POST['otaCode'];
 | 
			
		||||
	$otaSecret = $_POST['otaSecret'];
 | 
			
		||||
 | 
			
		||||
	$ga = new PHPGangsta_GoogleAuthenticator();
 | 
			
		||||
	$ota = $_SESSION['OTA'];
 | 
			
		||||
	$userName = $_SESSION['USERNAME'];
 | 
			
		||||
	$userPassword = $_SESSION['PASSWORD'];
 | 
			
		||||
	unset($_SESSION['OTA']);
 | 
			
		||||
	$checkResult = $ga->verifyCode($otaSecret, $otaCode, 6);    // 2 = 2*30sec clock tolerance
 | 
			
		||||
	if ($checkResult) {
 | 
			
		||||
		$landingPage = $userManager->login($userName, $userPassword);
 | 
			
		||||
		header('Location: ' . BASEDIR . $landingPage);
 | 
			
		||||
		echo 'OK';
 | 
			
		||||
	} else {
 | 
			
		||||
		echo 'FAILED';
 | 
			
		||||
	}
 | 
			
		||||
	//TODO: upravi a ověřit jeslti ja zabezpečené
 | 
			
		||||
	//TODO:
 | 
			
		||||
	die();
 | 
			
		||||
}
 | 
			
		||||
@@ -8,26 +8,16 @@
 | 
			
		||||
	<title><?php echo $TITLE ?></title>
 | 
			
		||||
</head>
 | 
			
		||||
<body class="no-transitions">
 | 
			
		||||
	<div class="modal-container">
 | 
			
		||||
		<div class="modal">
 | 
			
		||||
			<h4 class="mb-4">Login</h4>
 | 
			
		||||
			<form method="post">
 | 
			
		||||
				<div class="field">
 | 
			
		||||
					<div class="label">Name:</div>
 | 
			
		||||
					<input class="input" type="text" name="username" placeholder="Jméno.."/>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="field">
 | 
			
		||||
					<div class="label">Password:</div>
 | 
			
		||||
					<input class="input" type="password" name="password" placeholder="Heslo.."/>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="field">
 | 
			
		||||
					<div class="label">Remember me:</div>
 | 
			
		||||
					<input class="" type="checkbox" name="remember" value="true"/>
 | 
			
		||||
				</div>
 | 
			
		||||
				<input type="submit" class="button" name="login" value="Login"/>
 | 
			
		||||
			</form>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
	<?php
 | 
			
		||||
	if (isset($ota) && $ota != '') {	
 | 
			
		||||
		$partial = new Partial('loginOta');
 | 
			
		||||
		$partial->prepare('ota',$ota);
 | 
			
		||||
		$partial->render();
 | 
			
		||||
	} else {
 | 
			
		||||
		$partial = new Partial('loginForm');
 | 
			
		||||
		$partial->render();
 | 
			
		||||
	}
 | 
			
		||||
	?>
 | 
			
		||||
	
 | 
			
		||||
	<?php
 | 
			
		||||
	$partial = new Partial('footer');
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								app/templates/part/loginForm.phtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								app/templates/part/loginForm.phtml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
<div class="modal-container">
 | 
			
		||||
    <div class="modal">
 | 
			
		||||
        <h4 class="mb-4">Login</h4>
 | 
			
		||||
        <form method="post">
 | 
			
		||||
            <div class="field">
 | 
			
		||||
                <div class="label">Name:</div>
 | 
			
		||||
                <input class="input" type="text" name="username" placeholder="Jméno.."/>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="field">
 | 
			
		||||
                <div class="label">Password:</div>
 | 
			
		||||
                <input class="input" type="password" name="password" placeholder="Heslo.."/>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="field">
 | 
			
		||||
                <div class="label">Remember me:</div>
 | 
			
		||||
                <input class="" type="checkbox" name="remember" value="true"/>
 | 
			
		||||
            </div>
 | 
			
		||||
            <input type="submit" class="button" name="login" value="Login"/>
 | 
			
		||||
        </form>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										13
									
								
								app/templates/part/loginOta.phtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								app/templates/part/loginOta.phtml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
<div class="modal-container">
 | 
			
		||||
    <div class="modal">
 | 
			
		||||
        <h4 class="mb-4">OTA</h4>
 | 
			
		||||
        <form method="post">
 | 
			
		||||
            <input type="hidden" name="otaSecret" value="<?php echo $OTA; ?>"/>
 | 
			
		||||
            <div class="field">
 | 
			
		||||
                <div class="label">Code:</div>
 | 
			
		||||
                <input class="input" type="text" name="otaCode" placeholder=""/>
 | 
			
		||||
            </div>
 | 
			
		||||
            <input type="submit" class="button" name="login" value="Login"/>
 | 
			
		||||
        </form>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -76,6 +76,13 @@
 | 
			
		||||
					</div>
 | 
			
		||||
				</form>	
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="col-12 col-sm-9 mx-auto mt-4">
 | 
			
		||||
				<h4 class="mb-4"><?php ?></h4>
 | 
			
		||||
				<img src="<?php echo ''?>" />
 | 
			
		||||
				<form method="post">
 | 
			
		||||
					
 | 
			
		||||
				</form>	
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="col-12 col-sm-9 mx-auto mt-4">
 | 
			
		||||
				<h4 class="mb-4"><?php $LANGMNG->echo('t_createuser') ?></h4>
 | 
			
		||||
				<table class="table is-fluid">
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										252
									
								
								app/vendor/GoogleAuthenticator.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								app/vendor/GoogleAuthenticator.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,252 @@
 | 
			
		||||
<?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/
 | 
			
		||||
 */
 | 
			
		||||
class PHPGangsta_GoogleAuthenticator
 | 
			
		||||
{
 | 
			
		||||
    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();
 | 
			
		||||
 | 
			
		||||
        // Valid secret lengths are 80 to 640 bits
 | 
			
		||||
        if ($secretLength < 16 || $secretLength > 128) {
 | 
			
		||||
            throw new Exception('Bad secret length');
 | 
			
		||||
        }
 | 
			
		||||
        $secret = '';
 | 
			
		||||
        $rnd = false;
 | 
			
		||||
        if (function_exists('random_bytes')) {
 | 
			
		||||
            $rnd = random_bytes($secretLength);
 | 
			
		||||
        } elseif (function_exists('mcrypt_create_iv')) {
 | 
			
		||||
            $rnd = mcrypt_create_iv($secretLength, MCRYPT_DEV_URANDOM);
 | 
			
		||||
        } elseif (function_exists('openssl_random_pseudo_bytes')) {
 | 
			
		||||
            $rnd = openssl_random_pseudo_bytes($secretLength, $cryptoStrong);
 | 
			
		||||
            if (!$cryptoStrong) {
 | 
			
		||||
                $rnd = false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if ($rnd !== false) {
 | 
			
		||||
            for ($i = 0; $i < $secretLength; ++$i) {
 | 
			
		||||
                $secret .= $validChars[ord($rnd[$i]) & 31];
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new Exception('No source of secure random');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get QR-Code URL for image, from google charts.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $name
 | 
			
		||||
     * @param string $secret
 | 
			
		||||
     * @param string $title
 | 
			
		||||
     * @param array  $params
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getQRCodeGoogleUrl($name, $secret, $title = null, $params = array())
 | 
			
		||||
    {
 | 
			
		||||
        $width = !empty($params['width']) && (int) $params['width'] > 0 ? (int) $params['width'] : 200;
 | 
			
		||||
        $height = !empty($params['height']) && (int) $params['height'] > 0 ? (int) $params['height'] : 200;
 | 
			
		||||
        $level = !empty($params['level']) && array_search($params['level'], array('L', 'M', 'Q', 'H')) !== false ? $params['level'] : 'M';
 | 
			
		||||
 | 
			
		||||
        $urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.'');
 | 
			
		||||
        if (isset($title)) {
 | 
			
		||||
            $urlencoded .= urlencode('&issuer='.urlencode($title));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return "https://api.qrserver.com/v1/create-qr-code/?data=$urlencoded&size=${width}x${height}&ecc=$level";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (strlen($code) != 6) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for ($i = -$discrepancy; $i <= $discrepancy; ++$i) {
 | 
			
		||||
            $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i);
 | 
			
		||||
            if ($this->timingSafeEquals($calculatedCode, $code)) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the code length, should be >=6.
 | 
			
		||||
     *
 | 
			
		||||
     * @param int $length
 | 
			
		||||
     *
 | 
			
		||||
     * @return PHPGangsta_GoogleAuthenticator
 | 
			
		||||
     */
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A timing safe equals comparison
 | 
			
		||||
     * more info here: http://blog.ircmaxell.com/2014/11/its-all-about-time.html.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $safeString The internal (safe) value to be checked
 | 
			
		||||
     * @param string $userString The user submitted (unsafe) value
 | 
			
		||||
     *
 | 
			
		||||
     * @return bool True if the two strings are identical
 | 
			
		||||
     */
 | 
			
		||||
    private function timingSafeEquals($safeString, $userString)
 | 
			
		||||
    {
 | 
			
		||||
        if (function_exists('hash_equals')) {
 | 
			
		||||
            return hash_equals($safeString, $userString);
 | 
			
		||||
        }
 | 
			
		||||
        $safeLen = strlen($safeString);
 | 
			
		||||
        $userLen = strlen($userString);
 | 
			
		||||
 | 
			
		||||
        if ($userLen != $safeLen) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $result = 0;
 | 
			
		||||
 | 
			
		||||
        for ($i = 0; $i < $userLen; ++$i) {
 | 
			
		||||
            $result |= (ord($safeString[$i]) ^ ord($userString[$i]));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // They are only identical strings if $result is exactly 0...
 | 
			
		||||
        return $result === 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user