require_once 'handlers/db_hook.inc'; $l = openDB(); $r = mysqli_query($l, "SELECT password FROM `userdata` WHERE username='******'user']) . "'"); $hba = mysqli_fetch_array($r); $hb = $hba[0]; $a = json_decode($hb, true); print_r($a); require_once 'stronghash/php-stronghash.php'; require_once 'handlers/login_functions.php'; $h = new Stronghash(); $u = new UserFunctions(); // user validation echo "<pre> LookupUser results -- "; print_r($u->lookupUser($_POST['user'], $_POST['pw_base'], true)); echo "\n VerifyHash results --"; print_r($h->verifyHash($_POST['pw_base'], $a, null, null, null, true)); echo "</pre>"; } ?> <article> <?php require_once 'login.php'; echo $login_output; ?> <div> <h3>Test Hash</h3> <p>You can check to ensure the proper functioning of the hashing here. Please note these passwords in the next field are plaintext.</p> <form action='?t=hash' method='post'> <input type='email' name='user' placeholder='username'/><br/> <input type='text' name='pw_base' placeholder='pass'/><br/> <input type='submit'/>
public function lookupUser($username, $pw, $return = true, $totp_code = false, $override = false) { /*** * Primary function to check login validation. * * @param string|int $username a username looking at a column set by the * usercol of this object * @param string $pw the plaintext password of the user * @param bool $return whether to return user data, or just the * boolean lookup state * @param bool|int $totp_code - false if none is needed/given, * otherwise the code * @param bool $override ***/ if (strlen($pw) > 8192) { throw new Exception('Passwords must be less than 8192 characters in length.'); } # check it's a valid email! validation skipped. $xml = new Xml(); $result = $this->lookupItem($username, $this->userColumn); if ($result !== false) { try { $userdata = mysqli_fetch_assoc($result); if (is_numeric($userdata['id'])) { # check password if (!class_exists('Stronghash')) { require_once dirname(__FILE__) . '/../core/stronghash/php-stronghash.php'; } $hash = new Stronghash(); $data = json_decode($userdata[$this->pwColumn], true); $original_data = $data; $data['raw_db'] = $userdata[$this->pwColumn]; # Decrypt the password if totp_code is_numeric() if (is_numeric($totp_code)) { $pw = $this->decryptWithStoredKey($pw); } if (empty($data) || empty($data['salt'])) { try { # Try legacy $xml->setXml($userdata['data']); $data['algo'] = $xml->getTagContents('<algo>'); $data['rounds'] = $xml->getTagContents('<rounds>'); /* Default rounds for sha512 */ if (empty($data['rounds'])) { $data['rounds'] = 10000; } if (empty($data['algo'])) { $data['algo'] = 'sha512'; } $data['salt'] = null; $data['hash'] = $userdata['pass']; $data['legacy'] = true; $pw = $userdata['salt'] . $pw . $userdata['creation']; } catch (Exception $e) { return array(false, 'status' => false, 'message' => 'No valid password data'); } } if ($hash->verifyHash($pw, $data)) { $this->getUser($userdata[$this->userColumn]); ## Does the user have 2-factor authentication? if ($this->has2FA()) { $l = $this->openDB(); if (empty($totp_code)) { # The user has 2FA turned on, prompt it /* * Here we create a temporary, dummy * password for the client to save and * pass again. Since we're storing an * encrypted hash, even the data being * taken is no more "public" than it * already is in the password * column. However, it keeps us validating * a session in progress of TOTP-ing. */ $key = Stronghash::createSalt(); $query = 'UPDATE `' . $this->getTable() . '` SET `' . $this->tmpColumn . "`='{$key}' WHERE `" . $this->userColumn . "`='" . $this->username . "'"; $r = mysqli_query($l, $query); if ($r === false) { throw new Exception('Unable to encrypt password'); } $encrypted_pw = urlencode(self::encryptThis($key, $pw, $this->getIV())); # Encrypt the keys to validate the user asynchronously # Of course, this this was called asynchronously, the keys will be empty ... $cookiekey = $this->domain . '_secret'; #$cookiekey=str_replace(".","_",$this->domain)."_secret"; $cookieauth = $this->domain . '_auth'; #$cookieauth=str_replace(".","_",$this->domain)."_auth"; $encrypted_secret = self::encryptThis($key, $_COOKIE[$cookiekey], $this->getIV()); $encrypted_hash = self::encryptThis($key, $_COOKIE[$cookieauth], $this->getIV()); return array(false, 'status' => false, 'totp' => true, 'error' => false, 'human_error' => 'Please enter the code generated by the authenticator application on your device.', 'encrypted_password' => $encrypted_pw, 'encrypted_secret' => $encrypted_secret, 'encrypted_hash' => $encrypted_hash); } if ($this->verifyTOTP($totp_code) !== true) { # Bad TOTP code return array(false, 'status' => false, 'totp' => true, 'error' => 'Invalid TOTP code', 'human_error' => 'Bad verification code. Please try again.'); } # Remove the encryption key $query = 'UPDATE `' . $this->getTable() . '` SET `' . $this->tmpColumn . "`='' WHERE `" . $this->userColumn . "`='" . $this->username . "'"; mysqli_query($l, $query); # Return decrypted userdata, if applicable # The salt is the password key "salt" $decname = self::decryptThis($data['salt'] . $pw, $userdata['name'], $this->getIV()); if (empty($decname)) { $decname = $userdata['name']; } if (!$return) { return array(true, $decname, 'status' => true); } else { $returning = array(true, $userdata, 'status' => true, 'data' => $userdata); return $returning; } } if (($userdata['flag'] || $override) && !$userdata['disabled']) { # This user is OK and not disabled, nor pending validation # Return decrypted userdata, if applicable # The salt is the password key "salt" $decname = self::decryptThis($data['salt'] . $pw, $userdata['name'], $this->getIV()); if (empty($decname)) { $decname = $userdata['name']; } if (!$return) { return array(true, $decname, 'status' => true); } else { $returning = array(true, $userdata, 'status' => true, 'data' => $userdata); return $returning; } } else { if (!$userdata['flag']) { return array(false, 'status' => false, 'message' => 'Your login information is correct, but your account is still being validated, or has been disabled. Please try again later.'); } if ($userdata['disabled']) { # do a time check if ($userdata['dtime'] + 3600 > self::microtime_float()) { $rem = intval($userdata['dtime']) - intval(self::microtime_float()) + 3600; $min = $rem % 60; $sec = $rem - 60 * $min; return array(false, 'status' => false, 'message' => 'Your account has been disabled for too many failed login attempts. Please try again in ' . $min . ' minutes and ' . $sec . ' seconds.'); } else { # Clear login disabled flag $query1 = 'UPDATE `' . $this->getTable() . '` SET `disabled`=false WHERE `id`=' . $userdata['id']; $l = $this->openDB(); $result = mysqli_query($l, $query1); } } # All checks passed. if (!$return) { $decname = self::decryptThis($salt . $pw, $userdata['name'], $this->getIV()); if (empty($decname)) { $decname = $userdata['name']; } return array(true, $decname, 'status' => true); } else { $userdata['img'] = $this->getUserPicture(); $returning = array(true, $userdata, 'status' => true, 'data' => $userdata); return $returning; } } } else { return array(false, 'status' => false, 'message' => 'Sorry, your username or password is incorrect.', 'error' => 'Bad Password'); # , "data" => $data, "check_results" => $hash->verifyHash($pw, $data, null, null, null, true) } # end good username loop } else { return array(false, 'status' => false, 'message' => 'Sorry, your username or password is incorrect.', 'error' => 'Bad username', 'desc' => 'No numeric id'); } } catch (Exception $e) { return array(false, 'status' => false, 'message' => 'System error. Please try again. If the problem persists, please report it.', 'error' => $e->getMessage()); } } else { return array(false, 'status' => false, 'message' => 'Sorry, your username or password is incorrect.', 'error' => 'Bad username', 'desc' => $result['error']); } }