Jump to content

Kevin

Members
  • Content count

    31
  • Joined

  • Last visited

  • Days Won

    11

Posts posted by Kevin


  1. Hey everyone, it's been a long time.

    Today I implemented bcrypt in sweater and I decided i'd teach you how to do it without downloading my version of sweater directly, because who knows ? maybe you have your own version and don't want to replace the whole files.

    I'll show you how to update everyone's passwords too, so no worries about that. (thanks to @Ben who helped me fix a bug so-to-speak).

     

    Alright, let's begin with what's pretty obvious, the Hashing file (Cryptography.php). What you have to do is, replace everything in there with this:

    Spoiler
    <?php
    
    namespace Sweater\Crypto;
    
    trait Cryptography {
    	
    	private $strCharacterSet = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789`[email protected]#$()_+-={}|[]:,.";
    
    	function generateRandomKey() {
    		$strKeyLength = mt_rand(7, 10);
    		$strRandomKey = "";
    
    		foreach(range(0, $strKeyLength) as $strCurrentLength) {
    			$strRandomKey .= substr($this->strCharacterSet, mt_rand(0, strlen($this->strCharacterSet)), 1);
    		}
    
    		return $strRandomKey;
    	}
    
    	function encryptPassword($strPassword, $strMD5 = true) {
    		if($strMD5 !== false) {
    			$strPassword = md5($strPassword);
    		}
    
    		$strHash = substr($strPassword, 16, 16) . substr($strPassword, 0, 16);
    		return $strHash;
    	}
    
    	function getLoginHash($strPassword, $strRandomKey) {
    		$strHash = $this->encryptPassword($strPassword, false);
    		$strHash .= $strRandomKey;
    		$strHash .= 'Y(02.>\'H}t":E1';
    		$strHash = $this->encryptPassword($strHash);
    
    		return $strHash;
    	}
    	
    }
    
    ?>

    Yeah I know, it looks like kitsune's now :)

    Next, you probably want to set this in Client.php to public:

    private $strRandomKey;

    Change that to public.

    Now, since I don't think any of you have modified the login handler in sweater, or if you did then you're competent enough to see what I modified in the handleLogin and replace the stuff.

    If you didn't modify it, replace this whole function with yours in LoginHandler.php:

    Spoiler
    function handleLogin($arrData, Client $objClient){
    		$strUser = $arrData['body']['login']['nick'];
    		$strPass = $arrData['body']['login']['pword'];
    		Silk\Logger::Log('Client is attempting to login with username \'' . $strUser . '\'');
    		$blnExist = $this->objDatabase->playerExists($strUser);
    		if($blnExist === false){
    			$objClient->sendError(100);
    			return $this->removeClient($objClient->resSocket);
    		}
    		$arrUser = $this->objDatabase->getRow($strUser);
    		$intUser = $arrUser['ID'];
    		$strPassword = $arrUser['Password'];
    		$strRandom = $objClient->getRandomKey();
    		if($this->arrServer['Type'] == 'Login'){
    			Silk\Logger::Log('Handling login hashing', 'DEBUG');
    			$strUppedPass = strtoupper($strPassword);
    			$strEncrypt = $strUppedPass;
    			if(password_verify($strPass, $strPassword) !== true) {
    				Silk\Logger::Log('Failed login attempt for user \'' . $strUser . '\'', Silk\Logger::Debug);
    				$objClient->sendError(101);
    				$this->removeClient($objClient->resSocket);
    			}elseif($arrUser['ID'] > 99999999999){
    				Silk\Logger::Log('Failed login attempt for user \'' . $strUser . '\'', Silk\Logger::Debug);
    				$objClient->sendError(101);
    				$this->removeClient($objClient->resSocket);
    			}else {
    				$objClient->sendXt('sd', -1, $this->getServers());
    				$strHash = md5(strrev($objClient->strRandomKey));
    				Silk\Logger::Log('Random string: ' . $strHash);
    				$objClient->arrBuddies = json_decode($arrUser['Buddies'], true);
    				$strServers = $this->objDatabase->getServerPopulation();
    				$intBuddiesOnline = $this->objDatabase->getOnlineBuddiesCount($objClient);
    				$objClient->sendXt('l', -1, $intUser, $strHash, '', $strServers);
    				$this->objDatabase->updateColumn($intUser, 'LoginKey', $strHash);
    				$this->removeClient($objClient->resSocket);
    				Silk\Logger::Log('User \'' . $strUser . '\' has successfully logged in!', Silk\Logger::Debug);
    			}
    		} else {
    			Silk\Logger::Log('Handling game hashing', Silk\Logger::Debug);
    			$strLoginKey = $this->objDatabase->getLoginKey($intUser);
    			$strHash = $this->encryptPassword($strLoginKey . $objClient->strRandomKey) . $strLoginKey;
    			if($strHash != $strLoginKey){
    				$objClient->sendXt('f#lb', -1, $strHash);
    				$objClient->sendXt('l', -1);
    				$objClient->setClient($arrUser);
    				$this->updateStats();
    			} else {
    				$objClient->sendError(101);
    				$this->removeClient($objClient->resSocket);
    			}
    		}
    	}

    Replace the handleRndK function too with this:

    function handleRndK($arrData, Client $objClient){
    	$objClient->strRandomKey = "e4a2dbcca10a7246817a83cd" . $objClient->strNickname;
    	$objClient->sendData('<msg t="sys"><body action="rndK" r="-1"><k>' . $objClient->strRandomKey . '</k></body></msg>'); 
    	$objClient->setRandomKey($objClient->strRandomKey);
    }

    You will also want to change your password column so our new password hash can fit in the column, so just run this SQL command:

    ALTER TABLE `users` CHANGE `Password` `Password` VARCHAR(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL COMMENT 'Password hash';

    Now you probably want to update everyone's md5 password to bcrypt, so you can simply run this PHP script:

    Spoiler
    <?php
    
    //Change this to conform to your database installation !
    $config = array(
      "Host" => "localhost",
      "Name" => "sweater",
      "User" => "root",
      "Pass" => ""
    );	
    
    $con = new PDO('mysql:host='. $config["Host"] . ';dbname=' . $config["Name"], $config["User"], $config["Pass"]);
    
    $passwords = $con->prepare('SELECT Password, ID FROM users');
    $passwords->execute();
    $getPasswords = $passwords->fetchAll();
    
    foreach($getPasswords as $password){
        $hashedPassword = strtoupper($password["Password"]);
        $userId = $password["ID"];
        $staticKey = 'e4a2dbcca10a7246817a83cd';
        $fancyPassword = getLoginHash($hashedPassword, $staticKey);
        
        $updatePasswords = $con->prepare('UPDATE users SET Password = :password WHERE ID = ' . $userId);
        $updatePasswords->bindValue(':password', $fancyPassword);
        $updatePasswords->execute();
    }
    
    function encryptPassword($password, $md5 = true) {
        if($md5 !== false) {
            $password = md5($password);
        }
    
        $hash = substr($password, 16, 16) . substr($password, 0, 16);
        return $hash;
    }
    
    function getLoginHash($password, $staticKey) {
        $hash = encryptPassword($password, false);
        $hash .= $staticKey;
        $hash .= 'Y(02.>\'H}t":E1';
        $hash = encryptPassword($hash);
        $hash = password_hash($hash, PASSWORD_DEFAULT, [ 'cost' => 12 ]);
    
        return $hash;
    }
    
    
    ?>

     

    For the register, since you are using sweater you are probably using my 'old' register, so use this new one.

    Once you've completed all of these, you're done. Hope this helped you. Cya.

    • Like 7

  2. I edited your topic to put your code inside the code tags, make sure you do so whenever you post code.

    Nice topic Zaseth, I am sure this will help a lot of users who want to make their own registration/login form.

×