<?php
/**
 * This file implements the salted md5 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.' );


load_class( 'users/model/passwords/_passworddriver.class.php', 'PasswordDriver' );

/**
 * saltedMd5PasswordDriver Class
 *
 * @package evocore
 */
class saltedMd5PasswordDriver extends PasswordDriver
{
	protected $code = 'bb$H';

	/**
	 * Length of salt
	 * @var integer
	 */
	protected $salt_length = 9;


	/**
	 * Hash password
	 *
	 * @param string Password
	 * @param string Salt
	 * @return string Hashed password
	 */
	public function hash( $password, $salt = '' )
	{
		if( empty( $salt ) )
		{	// Generate salt for new hashing:
			$salt = $this->get_random_salt();
		}

		if( ( $count_log2 = $this->get_count_setting( $salt ) ) === false )
		{
			// Return md5 of password if settings do not
			// comply with our standards. This will only
			// happen if pre-determined settings are
			// directly passed to the driver. The manager
			// will not do this. Same as the old hashing
			// implementation in phpBB 3.0
			return md5( $password );
		}

		$hash = md5( substr( $salt, 1 ).$password, true );
		do
		{
			$hash = md5( $hash.$password, true );
		}
		while( --$count_log2 );

		return $this->hash_encode64( $hash, 16 );
	}


	/**
	 * 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 )
	{
		$js_code = '
function salted_md5_hash_encode64( input, count )
{
	var output = "";
	var i = 0;
	var itoa64 = "'.$this->itoa64.'";

	do
	{
		value = input[ i++ ].charCodeAt(0);
		output += itoa64[ value & 0x3f ];

		if( i < count )
		{
			value |= input[ i ].charCodeAt(0) << 8;
		}

		output += itoa64[ (value >> 6) & 0x3f ];

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

		if( i < count )
		{
			value |= input[ i ].charCodeAt(0) << 16;
		}

		output += itoa64[ ( value >> 12 ) & 0x3f ];

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

		output += itoa64[ ( value >> 18 ) & 0x3f ];
	}
	while( i < count );

	return output;
}

function salted_md5_get_count_setting( salt )
{
	if( salt == "" || salt.length != '.$this->salt_length.' )
	{	// Wrong salt format:
		return false;
	}

	var itoa64 = "'.$this->itoa64.'";
	var count_log2 = itoa64.indexOf( salt[0] );

	if( count_log2 < 7 || count_log2 > 30 )
	{	// Not allowed value:
		return false;
	}

	return 1 << count_log2;
}

function salted_md5_hash( password, salt )
{
	var count_log2 = salted_md5_get_count_setting( salt );
	if( count_log2 === false )
	{
		return hex_md5( password );
	}

	var hash = rstr_md5( salt.substring( 1 ) + password );
	do
	{
		hash = rstr_md5( hash + password );
	}
	while( --count_log2 );

	return salted_md5_hash_encode64( hash, 16 );
}

salted_md5_hash( '.$var_password_name.', '.$var_salt_name.' );
';

		return $js_code;
	}


	/**
	 * Get a random salt value
	 *
	 * @return string Salt for password hashing
	 */
	protected function get_random_salt()
	{
		$count = 6;

		$random = generate_random_key( $count );

		$salt = $this->itoa64[ min( $count + 5, 30 ) ];
		$salt .= $this->hash_encode64( $random, $count );

		// Save last generated salt to know what value write in DB on user password updating:
		$this->last_generated_salt = $salt;

		return $salt;
	}


	/**
	 * Get setting count_log2 from salt to generate new hash
	 *
	 * @param string The salt that contains the setting in first char
	 *
	 * @return boolean|integer Containing the count_log2
	 *         or false if supplied salt is incorrect
	 */
	public function get_count_setting( $salt )
	{
		if( empty( $salt ) || strlen( $salt ) != $this->salt_length )
		{	// Wrong salt format:
			return false;
		}

		$count_log2 = strpos( $this->itoa64, $salt[0] );

		if( $count_log2 < 7 || $count_log2 > 30 )
		{	// Not allowed value:
			return false;
		}

		return 1 << $count_log2;
	}
}
?>