<?php
/**
 * This file implements the Password Driver class.
 *
 * This file is part of the evoCore framework - {@link http://evocore.net/}
 * See also {@link https://github.com/b2evolution/b2evolution}.
 *
 * @license GNU GPL v2 - {@link http://b2evolution.net/about/gnu-gpl-license}
 *
 * @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}
 *
 * @package evocore
 */
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );


/**
 * PasswordDriver Class
 *
 * @package evocore
 */
class PasswordDriver
{
	/**
	 * Length of salt
	 * @var integer
	 */
	protected $salt_length = 0;

	/**
	 * base64 alphabet
	 * @var string
	 */
	public $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

	/**
	 * Last salt value that was generated to store this in DB T_users -> user_salt
	 * @var string
	 */
	protected $last_generated_salt = '';


	/**
	 * Get code of the password driver
	 *
	 * @return string
	 */
	public function get_code()
	{
		return $this->code;
	}


	/**
	 * Get prefix of the password driver
	 * E.g. prefix of code `bb$2y` must be `$2y$`
	 * such prefix value is required to encrypt e.g. by password driver "bcrypt"
	 *
	 * @return string
	 */
	public function get_prefix()
	{
		return preg_replace( '#^[a-z]+#', '', $this->code ).'$';
	}


	/**
	 * Get last generated salt
	 *
	 * @return string
	 */
	public function get_last_generated_salt()
	{
		return $this->last_generated_salt;
	}


	/**
	* Check if hash type is supported
	*
	* @return boolean TRUE if supported, FALSE if not
	*/
	public function is_supported()
	{
		return true;
	}


	/**
	 * Hash password
	 *
	 * @param string Password
	 * @param string Salt
	 * @return string Hashed password
	 */
	public function hash( $password, $salt = '' )
	{
	}


	/**
	 * Check password against the supplied hash
	 *
	 * @param string Password
	 * @param string Salt
	 * @param string Hash
	 * @param boolean Is the password parameter already hashed?
	 * @return boolean TRUE if password is correct, else FALSE
	 */
	public function check( $password, $salt, $hash, $password_is_hashed = false )
	{
		if( ! $password_is_hashed )
		{	// If the checking password is not hashed try to do this by this password driver:
			$password = $this->hash( $password, $salt );
		}

		return $this->string_compare( $hash, $password );
	}


	/**
	 * Compare two strings byte by byte
	 *
	 * @param string The first string
	 * @param string The second string
	 *
	 * @return boolean TRUE if strings are the same, FALSE if not
	 */
	public function string_compare( $string_a, $string_b )
	{
		// Return if input variables are not strings or if length does not match
		if( !is_string( $string_a ) || !is_string( $string_b ) || strlen( $string_a ) != strlen( $string_b ) )
		{
			return false;
		}

		// Use hash_equals() if it's available:
		if( function_exists( 'hash_equals' ) )
		{
			return hash_equals( $string_a, $string_b );
		}

		$difference = 0;

		for( $i = 0; $i < strlen( $string_a ) && $i < strlen( $string_b ); $i++ )
		{
			$difference |= ord( $string_a[ $i ] ) ^ ord( $string_b[ $i ] );
		}

		return $difference === 0;
	}

	/**
	* Base64 encode hash
	*
	* @param string Input string
	* @param integer $count Input string length
	*
	* @return string base64 encoded string
	*/
	public function hash_encode64( $input, $count )
	{
		$output = '';
		$i = 0;

		do
		{
			$value = ord( $input[ $i++ ] );
			$output .= $this->itoa64[ $value & 0x3f ];

			if( $i < $count )
			{
				$value |= ord( $input[ $i ] ) << 8;
			}

			$output .= $this->itoa64[ ($value >> 6) & 0x3f ];

			if( $i++ >= $count )
			{
				break;
			}

			if( $i < $count )
			{
				$value |= ord( $input[ $i ] ) << 16;
			}

			$output .= $this->itoa64[ ( $value >> 12 ) & 0x3f ];

			if( $i++ >= $count )
			{
				break;
			}

			$output .= $this->itoa64[ ( $value >> 18 ) & 0x3f ];
		}
		while( $i < $count );

		return $output;
	}


	/**
	 * Extract a salt from hash
	 *
	 * @param string Full hash with prefix and salt
	 * @return string Salt
	 */
	public function extract_salt( $hash )
	{
		$salt = substr( $hash, strlen( $this->get_prefix() ), $this->salt_length );

		if( strlen( $salt ) != $this->salt_length )
		{
			return '';
		}

		return $salt;
	}


	/**
	 * Clear a hash from password driver prefix and password salt
	 *
	 * @param string Full hash with prefix and salt
	 * @return string Password without prefix and salt
	 */
	public function clear_hash( $hash )
	{
		// Remove the driver prefix and password salt from the generated hash:
		return substr( $hash, strlen( $this->get_prefix() ) + $this->salt_length );
	}


	/**
	 * Get JavaScript code to hash password on browser/client side
	 *
	 * @param string Name of password variable in JS code
	 * @param string Name of salt variable in JS code
	 * @return string
	 */
	public function get_javascript_hash_code( $var_password_name, $var_salt_name )
	{
		return '';
	}
}
?>