1869 lines
44 KiB
PHP
1869 lines
44 KiB
PHP
<?php
|
|
/**
|
|
* @brief Converter Login Handler
|
|
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
|
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
|
* @license http://www.invisionpower.com/legal/standards/
|
|
* @package IPS Social Suite
|
|
* @since 14 Oct 2014
|
|
* @version SVN_VERSION_NUMBER
|
|
*/
|
|
namespace IPS\Login;
|
|
|
|
/* To prevent PHP errors (extending class does not exist) revealing path */
|
|
if ( ! defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
|
{
|
|
header( ( isset( $_SERVER[ 'SERVER_PROTOCOL' ] ) ? $_SERVER[ 'SERVER_PROTOCOL' ] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
|
exit();
|
|
}
|
|
|
|
/**
|
|
* Converter Login Handler
|
|
*/
|
|
class _Convert extends LoginAbstract
|
|
{
|
|
/**
|
|
* @brief Icon
|
|
*/
|
|
public static $icon = 'arrow-right';
|
|
|
|
/**
|
|
* @brief Auth types
|
|
*/
|
|
public $authTypes = NULL;
|
|
|
|
/**
|
|
* Initiate
|
|
*
|
|
* @return void
|
|
*/
|
|
public function init()
|
|
{
|
|
$this->authTypes = $this->settings['auth_types'] ?: \IPS\Login::AUTH_TYPE_USERNAME + \IPS\Login::AUTH_TYPE_EMAIL;
|
|
}
|
|
|
|
/**
|
|
* Authenticate
|
|
*
|
|
* @param array $values Values from form
|
|
* @return \IPS\Member
|
|
* @throws \IPS\Login\Exception
|
|
*/
|
|
public function authenticate( $values )
|
|
{
|
|
/* Get member(s) */
|
|
$members = array();
|
|
if ( $this->authTypes & \IPS\Login::AUTH_TYPE_USERNAME )
|
|
{
|
|
$_member = \IPS\Member::load( $values[ 'auth' ], 'name', NULL );
|
|
if ( $_member->member_id )
|
|
{
|
|
$members[] = $_member;
|
|
}
|
|
}
|
|
if ( $this->authTypes & \IPS\Login::AUTH_TYPE_EMAIL )
|
|
{
|
|
$_member = \IPS\Member::load( $values[ 'auth' ], 'email' );
|
|
if ( $_member->member_id )
|
|
{
|
|
$members[] = $_member;
|
|
}
|
|
}
|
|
|
|
/* If we didn't match any, throw an exception */
|
|
if ( empty( $members ) )
|
|
{
|
|
throw new \IPS\Login\Exception( \IPS\Member::loggedIn()->language()->addToStack( 'login_err_no_account', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $this->getLoginType( $this->authTypes ) ) ) ) ), \IPS\Login\Exception::NO_ACCOUNT );
|
|
}
|
|
|
|
/* Table switcher for new IPS4 converters */
|
|
$table = 'conv_apps';
|
|
if ( \IPS\Db::i()->checkForTable( 'convert_apps' ) )
|
|
{
|
|
$table = 'convert_apps';
|
|
}
|
|
|
|
$apps = \IPS\Db::i()->select( 'app_key', $table, array( 'login=?', 1 ) );
|
|
|
|
foreach( $apps as $sw )
|
|
{
|
|
/* loop found members */
|
|
foreach( $members as $member )
|
|
{
|
|
/* Check the app method exists */
|
|
if ( !method_exists( $this, $sw ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( $this->$sw( $member, $values[ 'password' ] ) )
|
|
{
|
|
/* Update password and return */
|
|
if ( \IPS\Db::i()->checkForColumn( 'core_members', 'misc' ) )
|
|
{
|
|
$member->misc = "";
|
|
}
|
|
$member->conv_password = "";
|
|
$member->members_pass_salt = $member->generateSalt();
|
|
$member->members_pass_hash = $member->encryptedPassword( $values[ 'password' ] );
|
|
$member->save();
|
|
$member->memberSync( 'onPassChange', array( $values[ 'password' ] ) );
|
|
|
|
return $member;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Still here? Throw a password incorrect exception */
|
|
throw new \IPS\Login\Exception( 'login_err_bad_password', \IPS\Login\Exception::BAD_PASSWORD, NULL, $member );
|
|
}
|
|
|
|
/**
|
|
* ACP Settings Form
|
|
*
|
|
* @param string $url URL to redirect user to after successful submission
|
|
* @return array List of settings to save - settings will be stored to core_login_handlers.login_settings DB field
|
|
* @code
|
|
return array( 'savekey' => new \IPS\Helpers\Form\[Type]( ... ), ... );
|
|
* @endcode
|
|
*/
|
|
public function acpForm()
|
|
{
|
|
return array(
|
|
'auth_types' => new \IPS\Helpers\Form\Select( 'login_auth_types', $this->settings['auth_types'], TRUE, array( 'options' => array(
|
|
\IPS\Login::AUTH_TYPE_USERNAME => 'username',
|
|
\IPS\Login::AUTH_TYPE_EMAIL => 'email_address',
|
|
\IPS\Login::AUTH_TYPE_USERNAME + \IPS\Login::AUTH_TYPE_EMAIL => 'username_or_email',
|
|
) ) )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Can a member sign in with this login handler?
|
|
* Used to ensure when a user disassociates a social login that they have some other way of logging in
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @return bool
|
|
*/
|
|
public function canProcess( \IPS\Member $member )
|
|
{
|
|
// Though this is not entirely true, once a user logs in with the Convert method,
|
|
// a password for the Internal method is created for them so we want that to
|
|
// be being used and not for this method to be depended on
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Can a member change their email/password with this login handler?
|
|
*
|
|
* @param string $type 'email' or 'password'
|
|
* @param \IPS\Member $member The member
|
|
* @return bool
|
|
*/
|
|
public function canChange( $type, \IPS\Member $member )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* AEF
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function aef( $member, $password )
|
|
{
|
|
if ( \IPS\Login::compareHashes( $member->conv_password, md5( $member->misc . $password ) ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* BBPress Standalone
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function bbpress_standalone( $member, $password )
|
|
{
|
|
return $this->bbpress( $member, $password );
|
|
}
|
|
|
|
/**
|
|
* BBPress
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function bbpress( $member, $password )
|
|
{
|
|
$success = false;
|
|
$password = html_entity_decode( $password );
|
|
$hash = $member->conv_password;
|
|
|
|
if ( \strlen( $hash ) == 32 )
|
|
{
|
|
$success = ( bool ) ( \IPS\Login::compareHashes( md5( $password ), $member->conv_password ) );
|
|
}
|
|
|
|
// Nope, not md5.
|
|
if ( ! $success )
|
|
{
|
|
$itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|
$crypt = $this->hashCryptPrivate( $password, $hash, $itoa64, 'P' );
|
|
if ( $crypt[ 0 ] == '*' )
|
|
{
|
|
$crypt = crypt( $password, $hash );
|
|
}
|
|
|
|
if ( $crypt == $hash )
|
|
{
|
|
$success = true;
|
|
}
|
|
}
|
|
|
|
// Nope
|
|
if ( ! $success )
|
|
{
|
|
// No - check against WordPress.
|
|
// Note to self - perhaps push this to main bbpress method.
|
|
$success = $this->wordpress( $member, $password );
|
|
}
|
|
|
|
return $success;
|
|
}
|
|
|
|
/**
|
|
* BBPress 2.3
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function bbpress23( $member, $password )
|
|
{
|
|
return $this->bbpress( $member, $password );
|
|
}
|
|
|
|
/**
|
|
* Community Server
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function cs( $member, $password )
|
|
{
|
|
$hash = $member->conv_password;
|
|
|
|
$encodedHashPass = base64_encode( pack( "H*", sha1( base64_decode( $member->misc ) . $password ) ) );
|
|
$single_md5_pass = md5( $password );
|
|
|
|
if ( \IPS\Login::compareHashes( $encodedHashPass, $hash ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* CSAuth
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function CSAuth( $member, $password )
|
|
{
|
|
$wsdl = 'https://internal.auth.com/Service.asmx?wsdl';
|
|
$dest = 'https://interal.auth.com/Service.asmx';
|
|
$single_md5_pass = md5( $password );
|
|
|
|
try
|
|
{
|
|
$client = new SoapClient( $wsdl, array( 'trace' => 1 ) );
|
|
$client->__setLocation( $dest );
|
|
$loginparams = array( 'username' => $member->name, 'password' => $password );
|
|
$result = $client->AuthCS( $loginparams );
|
|
|
|
switch( $result->AuthCSResult )
|
|
{
|
|
case 'SUCCESS' :
|
|
return TRUE;
|
|
case 'WRONG_AUTH' :
|
|
return FALSE;
|
|
default :
|
|
return FALSE;
|
|
}
|
|
}
|
|
catch( Exception $ex )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Discuz
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function discuz( $member, $password )
|
|
{
|
|
if ( \IPS\Login::compareHashes( $member->conv_password, md5( md5( $password ) . $member->misc ) ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* FudForum
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function fudforum( $member, $password )
|
|
{
|
|
$success = false;
|
|
$single_md5_pass = md5( $password );
|
|
$hash = $member->conv_password;
|
|
|
|
if ( \strlen( $hash ) == 40 )
|
|
{
|
|
$success = ( \IPS\Login::compareHashes( sha1( $member->misc . sha1( $password ) ), $hash ) ) ? TRUE : FALSE;
|
|
}
|
|
else
|
|
{
|
|
$success = ( \IPS\Login::compareHashes( $single_md5_pass, $hash ) ) ? TRUE : FALSE;
|
|
}
|
|
|
|
return $success;
|
|
}
|
|
|
|
/**
|
|
* FusionBB
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function fusionbb( $member, $password )
|
|
{
|
|
/* FusionBB Has multiple methods that can be used to check a hash, so we need to cycle through them */
|
|
|
|
/* md5( md5( salt ) . md5( pass ) ) */
|
|
if ( \IPS\Login::compareHashes( md5( md5( $member->misc ) . md5( $password ) ), $member->conv_password ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/* md5( md5( salt ) . pass ) */
|
|
if ( \IPS\Login::compareHashes( md5( md5( $member->misc ) . $password ), $member->conv_password ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/* md5( pass ) */
|
|
if ( \IPS\Login::compareHashes( md5( $password ), $member->conv_password ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Ikonboard
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function ikonboard( $member, $password )
|
|
{
|
|
if ( \IPS\Login::compareHashes( $member->conv_password, crypt( $password, $member->misc ) ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
else if ( \IPS\Login::compareHashes( $member->conv_password, md5( $password . $member->name ) ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Joomla
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function joomla( $member, $password )
|
|
{
|
|
if ( \IPS\Login::compareHashes( $member->conv_password, md5( $password . $member->misc ) ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Kunena
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function kunena( $member, $password )
|
|
{
|
|
// Kunena authenticates using internal Joomla functions.
|
|
// This is required, however, if the member only converts from
|
|
// Kunena and not Joomla + Kunena.
|
|
return $this->joomla( $member, $password );
|
|
}
|
|
|
|
/**
|
|
* PhpBB
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function phpbb( $member, $password )
|
|
{
|
|
$password = html_entity_decode( $password );
|
|
$success = FALSE;
|
|
$hash = $member->conv_password;
|
|
|
|
/* phpBB 3.1 */
|
|
if( preg_match( '/^\$2[ay]\$(0[4-9]|[1-2][0-9]|3[0-1])\$[a-zA-Z0-9.\/]{53}/', $hash ) )
|
|
{
|
|
$ph = new PasswordHash( 8, TRUE );
|
|
$success = $ph->CheckPassword( $password, $member->conv_password ) ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
if ( $success === FALSE )
|
|
{
|
|
/* phpBB3 */
|
|
$single_md5_pass = md5( $password );
|
|
$itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|
|
|
if ( \strlen( $hash ) == 34 )
|
|
{
|
|
$success = ( $this->hashCryptPrivate( $password, $hash, $itoa64 ) === $hash ) ? TRUE : FALSE;
|
|
}
|
|
else
|
|
{
|
|
$success = ( \IPS\Login::compareHashes( $single_md5_pass, $hash ) ) ? TRUE : FALSE;
|
|
}
|
|
}
|
|
|
|
/* phpBB2 */
|
|
if ( ! $success )
|
|
{
|
|
$success = ( $this->hashCryptPrivate( $single_md5_hash, $hash, $itoa64 ) === $hash ? TRUE : FALSE );
|
|
}
|
|
|
|
return $success;
|
|
}
|
|
|
|
/**
|
|
* PHP Legacy (2.x)
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function phpbb_legacy( $member, $password )
|
|
{
|
|
return $this->phpbb( $member, $password );
|
|
}
|
|
|
|
/**
|
|
* Vanilla
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function vanilla( $member, $password )
|
|
{
|
|
if ( \IPS\Login::compareHashes( $member->conv_password, md5( md5( str_replace( ''', "'", html_entity_decode( $password ) ) ) . $member->misc ) ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Vbulletin 5.1
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function vb51connect( $member, $password )
|
|
{
|
|
/* Which do we need to use? */
|
|
$algo = explode( ':', $member->misc );
|
|
|
|
switch( $algo[ 0 ] )
|
|
{
|
|
case 'blowfish':
|
|
/* vBulletin uses PasswordHash, however they md5 once the password prior to checking */
|
|
$md5_once_password = md5( $password );
|
|
$ph = new PasswordHash( $algo[ 1 ], FALSE );
|
|
return $ph->CheckPassword( $md5_once_password, $member->conv_password );
|
|
break;
|
|
|
|
case 'legacy':
|
|
/* Legacy Passwords are stored in a format of HASH SALT so we need to explode on the space. */
|
|
$token = explode( ' ', $member->conv_password );
|
|
return $this->vbulletin( $member, $password, $token[ 1 ], $token[ 0 ] );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Vbulletin 5
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function vb5connect( $member, $password )
|
|
{
|
|
if ( strpos( $member->misc, 'blowfish' ) === FALSE and strpos( $member->misc, 'legacy' ) === FALSE )
|
|
{
|
|
return $this->vbulletin( $member, $password );
|
|
}
|
|
else
|
|
{
|
|
return $this->vb51connect( $member, $password );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Vbulletin 4
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function vbulletin( $member, $password, $salt = NULL, $hash = NULL )
|
|
{
|
|
if ( is_null( $salt ) )
|
|
{
|
|
$salt = $member->misc;
|
|
}
|
|
|
|
if ( is_null( $hash ) )
|
|
{
|
|
$hash = $member->conv_password;
|
|
}
|
|
|
|
$password = html_entity_decode( $password );
|
|
if ( \IPS\Login::compareHashes( $hash, md5( md5( str_replace( ''', "'", $password ) ) . $salt ) ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Vbulletin 3.8
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function vbulletin_legacy( $member, $password )
|
|
{
|
|
return $this->vbulletin( $member, $password );
|
|
}
|
|
|
|
/**
|
|
* Vbulletin 3.6
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function vbulletin_legacy36( $member, $password )
|
|
{
|
|
return $this->vbulletin( $member, $password );
|
|
}
|
|
|
|
/**
|
|
* MyBB
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function mybb( $member, $password )
|
|
{
|
|
if ( \IPS\Login::compareHashes( $member->conv_password, md5( md5( $member->misc ) . md5( $password ) ) ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SMF
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function smf( $member, $password )
|
|
{
|
|
if ( \IPS\Login::compareHashes( $member->conv_password, sha1( strtolower( $member->name ) . html_entity_decode( $password ) ) ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
else if ( \IPS\Login::compareHashes( $member->conv_password, sha1( strtolower( $member->name ) . $password ) ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
$ph = new PasswordHash(8, TRUE);
|
|
return $ph->CheckPassword( strtolower( $member->name ) . $password, $member->conv_password ) ? TRUE : FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SMF Legacy
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function smf_legacy( $member, $password )
|
|
{
|
|
if ( \IPS\Login::compareHashes( sha1( strtolower( $member->name ) . $password ), $member->conv_password ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Telligent
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function telligent_cs( $member, $password )
|
|
{
|
|
return $this->cs( $member, $password );
|
|
}
|
|
|
|
/**
|
|
* WoltLab 4.x
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function woltlab( $member, $password )
|
|
{
|
|
$testHash = FALSE;
|
|
|
|
/* If it's not blowfish, then we don't have a salt for it. */
|
|
if ( !preg_match( '/^\$2[ay]\$(0[4-9]|[1-2][0-9]|3[0-1])\$[a-zA-Z0-9.\/]{53}/', $member->conv_password ) )
|
|
{
|
|
$salt = mb_substr( $member->conv_password, 0, 29 );
|
|
$testHash = crypt( crypt( $password, $salt ), $salt );
|
|
}
|
|
|
|
if ( $testHash AND \IPS\Login::compareHashes( $member->conv_password, $testHash ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
elseif ( $this->woltlab_legacy( $member, $password ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* WoltLab 3.x
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function woltlab_legacy( $member, $password )
|
|
{
|
|
if ( \IPS\Login::compareHashes( $member->conv_password, sha1( $member->misc . sha1( $member->misc . sha1( $password ) ) ) ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* WebWiz
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function webwiz( $member, $password )
|
|
{
|
|
$success = FALSE;
|
|
|
|
if ( \IPS\Login::compareHashes( webWizAuth::HashEncode( $password . $member->misc ), $member->conv_password ) )
|
|
{
|
|
$success = TRUE;
|
|
}
|
|
|
|
return $success;
|
|
}
|
|
|
|
/**
|
|
* XenForo
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function xenforo( $member, $password )
|
|
{
|
|
$password = html_entity_decode( $password );
|
|
|
|
// XF 1.2
|
|
if ( $this->xenforo12( $member, $password ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// XF 1.1
|
|
if ( $this->xenforo11( $member, $password ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// If they converted vB > XF > IPB then passwords may be in vB format still - try that.
|
|
if ( $this->vbulletin( $member, $password ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* XenForo 1.2
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function xenforo12( $member, $password )
|
|
{
|
|
if ( !isset( \IPS\Settings::i()->xenforo_password_iterations ) )
|
|
{
|
|
\IPS\Settings::i()->xenforo_password_iterations = 10;
|
|
}
|
|
|
|
$ph = new PasswordHash( \IPS\Settings::i()->xenforo_password_iterations, false );
|
|
|
|
if ( $ph->CheckPassword( $password, $member->conv_password ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* XenForo 1.1
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function xenforo11( $member, $password )
|
|
{
|
|
if ( extension_loaded( 'hash' ) )
|
|
{
|
|
$hashedPassword = hash( 'sha256', hash( 'sha256', $password ) . $member->misc );
|
|
}
|
|
else
|
|
{
|
|
$hashedPassword = sha1( sha1( $password ) . $member->misc );
|
|
}
|
|
|
|
if ( \IPS\Login::compareHashes( $member->conv_password, $hashedPassword ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* PHP Fusion
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function phpfusion( $member, $password )
|
|
{
|
|
return ( bool ) \IPS\Login::compareHashes( md5( md5( $password ) ), $member->conv_password );
|
|
}
|
|
|
|
/**
|
|
* fluxBB
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function fluxbb( $member, $password )
|
|
{
|
|
$success = false;
|
|
$hash = $member->conv_password;
|
|
|
|
if ( \strlen( $hash ) == 40 )
|
|
{
|
|
if ( \IPS\Login::compareHashes( $hash, sha1( $member->misc . sha1( $password ) ) ) )
|
|
{
|
|
$success = TRUE;
|
|
}
|
|
elseif ( \IPS\Login::compareHashes( $hash, sha1( $password ) ) )
|
|
{
|
|
$success = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$success = ( \IPS\Login::compareHashes( md5( $password ), $hash ) ) ? TRUE : FALSE;
|
|
}
|
|
|
|
return $success;
|
|
}
|
|
|
|
/**
|
|
* punBB
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function punbb( $member, $password )
|
|
{
|
|
$success = false;
|
|
$hash = $member->conv_password;
|
|
|
|
if ( \strlen( $hash ) == 40 )
|
|
{
|
|
if ( \IPS\Login::compareHashes( $hash, sha1( $member->misc . sha1( $password ) ) ) )
|
|
{
|
|
$success = TRUE;
|
|
}
|
|
elseif ( \IPS\Login::compareHashes( $hash, sha1( $password ) ) )
|
|
{
|
|
$success = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$success = ( md5( $password ) == $hash ) ? TRUE : FALSE;
|
|
}
|
|
|
|
return $success;
|
|
}
|
|
|
|
/**
|
|
* Simplepress Forum
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function simplepress( $member, $password )
|
|
{
|
|
return $this->wordpress( $member, $password );
|
|
}
|
|
|
|
/**
|
|
* UBB Threads
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function ubbthreads( $member, $password )
|
|
{
|
|
$hash = $member->members_pass_hash;
|
|
$salt = $member->members_pass_salt;
|
|
|
|
if ( \IPS\Login::compareHashes( md5( $password ), $hash ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// Not using md5, UBB salts the password with the password
|
|
// IPB already md5'd it though, *sigh*
|
|
if ( \IPS\Login::compareHashes( md5( md5( $salt ) . crypt( $password, $password ) ), $hash ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// Now standard IPB check.
|
|
if ( \IPS\Login::compareHashes( md5( md5( $salt ) . md5( $password ) ), $hash ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Wordpress
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $password Password from form
|
|
* @return bool
|
|
*/
|
|
protected function wordpress( $member, $password )
|
|
{
|
|
$success = FALSE;
|
|
|
|
$password = ( trim( $_POST[ 'password' ] ) != '' ) ? trim( $_POST[ 'password' ] ) : $password;
|
|
|
|
// If the hash is still md5...
|
|
if ( \strlen( $member->conv_password ) <= 32 )
|
|
{
|
|
$success = ( \IPS\Login::compareHashes( $member->conv_password, md5( $password ) ) ) ? TRUE : FALSE;
|
|
}
|
|
// New pass hash check
|
|
else
|
|
{
|
|
// Init the pass class
|
|
$ph = new PasswordHash( 8, TRUE );
|
|
|
|
// Check it
|
|
$success = $ph->CheckPassword( $password, $member->conv_password ) ? TRUE : FALSE;
|
|
}
|
|
|
|
return $success;
|
|
}
|
|
|
|
/**
|
|
* Private crypt hashing for phpBB 3
|
|
*
|
|
* @access public
|
|
* @param string Password
|
|
* @param string Settings
|
|
* @param string Hash-lookup
|
|
* @return string phpBB3 password hash
|
|
*/
|
|
protected function hashCryptPrivate( $password, $setting, &$itoa64 )
|
|
{
|
|
$output = '*';
|
|
|
|
// Check for correct hash
|
|
if ( substr( $setting, 0, 3 ) != '$H$' )
|
|
{
|
|
return $output;
|
|
}
|
|
|
|
$count_log2 = strpos( $itoa64, $setting[3] );
|
|
|
|
if ( $count_log2 < 7 || $count_log2 > 30 )
|
|
{
|
|
return $output;
|
|
}
|
|
|
|
$count = 1 << $count_log2;
|
|
$salt = substr( $setting, 4, 8 );
|
|
|
|
if ( \strlen($salt) != 8 )
|
|
{
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* We're kind of forced to use MD5 here since it's the only
|
|
* cryptographic primitive available in all versions of PHP
|
|
* currently in use. To implement our own low-level crypto
|
|
* in PHP would result in much worse performance and
|
|
* consequently in lower iteration counts and hashes that are
|
|
* quicker to crack (by non-PHP code).
|
|
*/
|
|
if ( PHP_VERSION >= 5 )
|
|
{
|
|
$hash = md5( $salt . $password, true );
|
|
|
|
do
|
|
{
|
|
$hash = md5( $hash . $password, true );
|
|
}
|
|
while ( --$count );
|
|
}
|
|
else
|
|
{
|
|
$hash = pack( 'H*', md5( $salt . $password ) );
|
|
|
|
do
|
|
{
|
|
$hash = pack( 'H*', md5( $hash . $password ) );
|
|
}
|
|
while ( --$count );
|
|
}
|
|
|
|
$output = substr( $setting, 0, 12 );
|
|
$output .= $this->_hashEncode64( $hash, 16, $itoa64 );
|
|
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Private function to encode phpBB3 hash
|
|
*
|
|
* @access protected
|
|
* @param string Input
|
|
* @param count Iteration
|
|
* @param string Hash-lookup
|
|
* @return string phpbb3 password hash encoded bit
|
|
*/
|
|
protected function _hashEncode64($input, $count, &$itoa64)
|
|
{
|
|
$output = '';
|
|
$i = 0;
|
|
|
|
do
|
|
{
|
|
$value = ord( $input[$i++] );
|
|
$output .= $itoa64[$value & 0x3f];
|
|
|
|
if ( $i < $count )
|
|
{
|
|
$value |= ord($input[$i]) << 8;
|
|
}
|
|
|
|
$output .= $itoa64[($value >> 6) & 0x3f];
|
|
|
|
if ( $i++ >= $count )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( $i < $count )
|
|
{
|
|
$value |= ord($input[$i]) << 16;
|
|
}
|
|
|
|
$output .= $itoa64[($value >> 12) & 0x3f];
|
|
|
|
if ($i++ >= $count)
|
|
{
|
|
break;
|
|
}
|
|
|
|
$output .= $itoa64[($value >> 18) & 0x3f];
|
|
}
|
|
while ( $i < $count );
|
|
|
|
return $output;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Portable PHP password hashing framework.
|
|
* @package phpass
|
|
* @since 2.5
|
|
* @version 0.1
|
|
* @link http://www.openwall.com/phpass/
|
|
*/
|
|
|
|
#
|
|
# Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
|
|
# the public domain.
|
|
#
|
|
# There's absolutely no warranty.
|
|
#
|
|
# Please be sure to update the Version line if you edit this file in any way.
|
|
# It is suggested that you leave the main version number intact, but indicate
|
|
# your project name (after the slash) and add your own revision information.
|
|
#
|
|
# Please do not change the "private" password hashing method implemented in
|
|
# here, thereby making your hashes incompatible. However, if you must, please
|
|
# change the hash type identifier (the "$P$") to something different.
|
|
#
|
|
# Obviously, since this code is in the public domain, the above are not
|
|
# requirements (there can be none), but merely suggestions.
|
|
#
|
|
|
|
|
|
// This is needed for the SimplePress Forum login and for any other login based on this class (wordpress, bbpress, etc)
|
|
class PasswordHash
|
|
{
|
|
var $itoa64;
|
|
var $iteration_count_log2;
|
|
var $portable_hashes;
|
|
var $random_state;
|
|
function PasswordHash( $iteration_count_log2, $portable_hashes )
|
|
{
|
|
$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|
|
|
if ( $iteration_count_log2 < 4 || $iteration_count_log2 > 31 )
|
|
$iteration_count_log2 = 8;
|
|
$this->iteration_count_log2 = $iteration_count_log2;
|
|
|
|
$this->portable_hashes = $portable_hashes;
|
|
|
|
$this->random_state = microtime() . ( function_exists( 'getmypid' ) ? getmypid() : '' ) . uniqid( rand(), TRUE );
|
|
}
|
|
function get_random_bytes( $count )
|
|
{
|
|
$output = '';
|
|
if ( ( $fh = @fopen( '/dev/urandom', 'rb' ) ) )
|
|
{
|
|
$output = fread( $fh, $count );
|
|
fclose( $fh );
|
|
}
|
|
|
|
if ( \strlen( $output ) < $count )
|
|
{
|
|
$output = '';
|
|
for( $i = 0 ; $i < $count ; $i += 16 )
|
|
{
|
|
$this->random_state = md5( microtime() . $this->random_state );
|
|
$output .= pack( 'H*', md5( $this->random_state ) );
|
|
}
|
|
$output = substr( $output, 0, $count );
|
|
}
|
|
|
|
return $output;
|
|
}
|
|
function 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;
|
|
}
|
|
function gensalt_private( $input )
|
|
{
|
|
$output = '$P$';
|
|
$output .= $this->itoa64[ min( $this->iteration_count_log2 + ( ( PHP_VERSION >= '5' ) ? 5 : 3 ), 30 ) ];
|
|
$output .= $this->encode64( $input, 6 );
|
|
|
|
return $output;
|
|
}
|
|
function crypt_private( $password, $setting )
|
|
{
|
|
$output = '*0';
|
|
if ( substr( $setting, 0, 2 ) == $output )
|
|
$output = '*1';
|
|
|
|
if ( substr( $setting, 0, 3 ) != '$P$' )
|
|
return $output;
|
|
|
|
$count_log2 = strpos( $this->itoa64, $setting[ 3 ] );
|
|
if ( $count_log2 < 7 || $count_log2 > 30 )
|
|
return $output;
|
|
|
|
$count = 1 << $count_log2;
|
|
|
|
$salt = substr( $setting, 4, 8 );
|
|
if ( \strlen( $salt ) != 8 )
|
|
return $output;
|
|
|
|
# We're kind of forced to use MD5 here since it's the only
|
|
# cryptographic primitive available in all versions of PHP
|
|
# currently in use. To implement our own low-level crypto
|
|
# in PHP would result in much worse performance and
|
|
# consequently in lower iteration counts and hashes that are
|
|
# quicker to crack (by non-PHP code).
|
|
if ( PHP_VERSION >= '5' )
|
|
{
|
|
$hash = md5( $salt . $password, TRUE );
|
|
do
|
|
{
|
|
$hash = md5( $hash . $password, TRUE );
|
|
}
|
|
while ( -- $count );
|
|
}
|
|
else
|
|
{
|
|
$hash = pack( 'H*', md5( $salt . $password ) );
|
|
do
|
|
{
|
|
$hash = pack( 'H*', md5( $hash . $password ) );
|
|
}
|
|
while ( -- $count );
|
|
}
|
|
|
|
$output = substr( $setting, 0, 12 );
|
|
$output .= $this->encode64( $hash, 16 );
|
|
|
|
return $output;
|
|
}
|
|
function gensalt_extended( $input )
|
|
{
|
|
$count_log2 = min( $this->iteration_count_log2 + 8, 24 );
|
|
# This should be odd to not reveal weak DES keys, and the
|
|
# maximum valid value is (2**24 - 1) which is odd anyway.
|
|
$count = ( 1 << $count_log2 ) - 1;
|
|
|
|
$output = '_';
|
|
$output .= $this->itoa64[ $count & 0x3f ];
|
|
$output .= $this->itoa64[ ( $count >> 6 ) & 0x3f ];
|
|
$output .= $this->itoa64[ ( $count >> 12 ) & 0x3f ];
|
|
$output .= $this->itoa64[ ( $count >> 18 ) & 0x3f ];
|
|
|
|
$output .= $this->encode64( $input, 3 );
|
|
|
|
return $output;
|
|
}
|
|
function gensalt_blowfish( $input )
|
|
{
|
|
# This one needs to use a different order of characters and a
|
|
# different encoding scheme from the one in encode64() above.
|
|
# We care because the last character in our encoded string will
|
|
# only represent 2 bits. While two known implementations of
|
|
# bcrypt will happily accept and correct a salt string which
|
|
# has the 4 unused bits set to non-zero, we do not want to take
|
|
# chances and we also do not want to waste an additional byte
|
|
# of entropy.
|
|
$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
|
|
$output = '$2a$';
|
|
$output .= chr( ord( '0' ) + $this->iteration_count_log2 / 10 );
|
|
$output .= chr( ord( '0' ) + $this->iteration_count_log2 % 10 );
|
|
$output .= '$';
|
|
|
|
$i = 0;
|
|
do
|
|
{
|
|
$c1 = ord( $input[ $i ++ ] );
|
|
$output .= $itoa64[ $c1 >> 2 ];
|
|
$c1 = ( $c1 & 0x03 ) << 4;
|
|
if ( $i >= 16 )
|
|
{
|
|
$output .= $itoa64[ $c1 ];
|
|
break;
|
|
}
|
|
|
|
$c2 = ord( $input[ $i ++ ] );
|
|
$c1 |= $c2 >> 4;
|
|
$output .= $itoa64[ $c1 ];
|
|
$c1 = ( $c2 & 0x0f ) << 2;
|
|
|
|
$c2 = ord( $input[ $i ++ ] );
|
|
$c1 |= $c2 >> 6;
|
|
$output .= $itoa64[ $c1 ];
|
|
$output .= $itoa64[ $c2 & 0x3f ];
|
|
}
|
|
while ( 1 );
|
|
|
|
return $output;
|
|
}
|
|
function HashPassword( $password )
|
|
{
|
|
$random = '';
|
|
|
|
if ( CRYPT_BLOWFISH == 1 && ! $this->portable_hashes )
|
|
{
|
|
$random = $this->get_random_bytes( 16 );
|
|
$hash = crypt( $password, $this->gensalt_blowfish( $random ) );
|
|
if ( \strlen( $hash ) == 60 )
|
|
return $hash;
|
|
}
|
|
|
|
if ( CRYPT_EXT_DES == 1 && ! $this->portable_hashes )
|
|
{
|
|
if ( \strlen( $random ) < 3 )
|
|
$random = $this->get_random_bytes( 3 );
|
|
$hash = crypt( $password, $this->gensalt_extended( $random ) );
|
|
if ( \strlen( $hash ) == 20 )
|
|
return $hash;
|
|
}
|
|
|
|
if ( \strlen( $random ) < 6 )
|
|
$random = $this->get_random_bytes( 6 );
|
|
$hash = $this->crypt_private( $password, $this->gensalt_private( $random ) );
|
|
if ( \strlen( $hash ) == 34 )
|
|
return $hash;
|
|
|
|
# Returning '*' on error is safe here, but would _not_ be safe
|
|
# in a crypt(3)-like function used _both_ for generating new
|
|
# hashes and for validating passwords against existing hashes.
|
|
return '*';
|
|
}
|
|
function CheckPassword( $password, $stored_hash )
|
|
{
|
|
$hash = $this->crypt_private( $password, $stored_hash );
|
|
if ( $hash[ 0 ] == '*' )
|
|
$hash = crypt( $password, $stored_hash );
|
|
|
|
return $hash == $stored_hash;
|
|
}
|
|
}
|
|
class webWizAuth
|
|
{
|
|
static public function HashEncode( $strSecret )
|
|
{
|
|
if ( \strlen( $strSecret ) == 0 || \strlen( $strSecret ) >= pow( 2, 61 ) )
|
|
{
|
|
return "0000000000000000000000000000000000000000";
|
|
break;
|
|
}
|
|
|
|
//Initial Hex words are used for encoding Digest.
|
|
//These can be any valid 8-digit hex value (0 to F)
|
|
$strH[ 0 ] = "FB0C14C2";
|
|
$strH[ 1 ] = "9F00AB2E";
|
|
$strH[ 2 ] = "991FFA67";
|
|
$strH[ 3 ] = "76FA2C3F";
|
|
$strH[ 4 ] = "ADE426FA";
|
|
|
|
for( $intPos = 1 ; $intPos <= \strlen( $strSecret ) ; $intPos = $intPos + 56 )
|
|
{
|
|
$strEncode = substr( $strSecret, $intPos - 1, 56 ); //get 56 character chunks
|
|
$strEncode = self::WordToBinary( $strEncode ); //convert to binary
|
|
$strEncode = self::PadBinary( $strEncode ); //make it 512 bites
|
|
$strEncode = self::BlockToHex( $strEncode ); //convert to hex value
|
|
|
|
|
|
//Encode the hex value using the previous runs digest
|
|
//If it is the first run then use the initial values above
|
|
$strEncode = self::DigestHex( $strEncode, $strH[ 0 ], $strH[ 1 ], $strH[ 2 ], $strH[ 3 ], $strH[ 4 ] );
|
|
|
|
//Combine the old digest with the new digest
|
|
$strH[ 0 ] = self::HexAdd( substr( $strEncode, 0, 8 ), $strH[ 0 ] );
|
|
$strH[ 1 ] = self::HexAdd( substr( $strEncode, 8, 8 ), $strH[ 1 ] );
|
|
$strH[ 2 ] = self::HexAdd( substr( $strEncode, 16, 8 ), $strH[ 2 ] );
|
|
$strH[ 3 ] = self::HexAdd( substr( $strEncode, 24, 8 ), $strH[ 3 ] );
|
|
$strH[ 4 ] = self::HexAdd( substr( $strEncode, \strlen( $strEncode ) - ( 8 ) ), $strH[ 4 ] );
|
|
}
|
|
|
|
//This is the final Hex Digest
|
|
$function_ret = $strH[ 0 ] . $strH[ 1 ] . $strH[ 2 ] . $strH[ 3 ] . $strH[ 4 ];
|
|
|
|
return $function_ret;
|
|
}
|
|
static protected function HexToBinary( $btHex )
|
|
{
|
|
switch( $btHex )
|
|
{
|
|
case "0" :
|
|
$function_ret = "0000";
|
|
break;
|
|
case "1" :
|
|
$function_ret = "0001";
|
|
break;
|
|
case "2" :
|
|
$function_ret = "0010";
|
|
break;
|
|
case "3" :
|
|
$function_ret = "0011";
|
|
break;
|
|
case "4" :
|
|
$function_ret = "0100";
|
|
break;
|
|
case "5" :
|
|
$function_ret = "0101";
|
|
break;
|
|
case "6" :
|
|
$function_ret = "0110";
|
|
break;
|
|
case "7" :
|
|
$function_ret = "0111";
|
|
break;
|
|
case "8" :
|
|
$function_ret = "1000";
|
|
break;
|
|
case "9" :
|
|
$function_ret = "1001";
|
|
break;
|
|
case "A" :
|
|
$function_ret = "1010";
|
|
break;
|
|
case "B" :
|
|
$function_ret = "1011";
|
|
break;
|
|
case "C" :
|
|
$function_ret = "1100";
|
|
break;
|
|
case "D" :
|
|
$function_ret = "1101";
|
|
break;
|
|
case "E" :
|
|
$function_ret = "1110";
|
|
break;
|
|
case "F" :
|
|
$function_ret = "1111";
|
|
break;
|
|
default :
|
|
|
|
$function_ret = "2222";
|
|
break;
|
|
}
|
|
return $function_ret;
|
|
}
|
|
static protected function BinaryToHex( $strBinary )
|
|
{
|
|
switch( $strBinary )
|
|
{
|
|
case "0000" :
|
|
$function_ret = "0";
|
|
break;
|
|
case "0001" :
|
|
$function_ret = "1";
|
|
break;
|
|
case "0010" :
|
|
$function_ret = "2";
|
|
break;
|
|
case "0011" :
|
|
$function_ret = "3";
|
|
break;
|
|
case "0100" :
|
|
$function_ret = "4";
|
|
break;
|
|
case "0101" :
|
|
$function_ret = "5";
|
|
break;
|
|
case "0110" :
|
|
$function_ret = "6";
|
|
break;
|
|
case "0111" :
|
|
$function_ret = "7";
|
|
break;
|
|
case "1000" :
|
|
$function_ret = "8";
|
|
break;
|
|
case "1001" :
|
|
$function_ret = "9";
|
|
break;
|
|
case "1010" :
|
|
$function_ret = "A";
|
|
break;
|
|
case "1011" :
|
|
$function_ret = "B";
|
|
break;
|
|
case "1100" :
|
|
$function_ret = "C";
|
|
break;
|
|
case "1101" :
|
|
$function_ret = "D";
|
|
break;
|
|
case "1110" :
|
|
$function_ret = "E";
|
|
break;
|
|
case "1111" :
|
|
$function_ret = "F";
|
|
break;
|
|
default :
|
|
|
|
$function_ret = "Z";
|
|
break;
|
|
}
|
|
return $function_ret;
|
|
}
|
|
static protected function WordToBinary( $strWord )
|
|
{
|
|
$strBinary = '';
|
|
for( $intPos = 1 ; $intPos <= \strlen( $strWord ) ; $intPos = $intPos + 1 )
|
|
{
|
|
$strTemp = substr( $strWord, intval( $intPos ) - 1, 1 );
|
|
$strBinary = $strBinary . self::IntToBinary( ord( $strTemp ) );
|
|
}
|
|
|
|
return $strBinary;
|
|
}
|
|
static protected function IntToBinary( $intNum )
|
|
{
|
|
$intNew = $intNum;
|
|
$strBinary = '';
|
|
while ( $intNew > 1 )
|
|
{
|
|
$dblNew = doubleval( $intNew ) / 2;
|
|
$intNew = round( doubleval( $dblNew ) - 0.1, 0 );
|
|
if ( doubleval( $dblNew ) == doubleval( $intNew ) )
|
|
{
|
|
$strBinary = "0" . $strBinary;
|
|
}
|
|
else
|
|
{
|
|
$strBinary = "1" . $strBinary;
|
|
}
|
|
}
|
|
|
|
$strBinary = $intNew . $strBinary;
|
|
$intTemp = \strlen( $strBinary ) % 8;
|
|
|
|
for( $intNew = $intTemp ; $intNew <= 7 ; $intNew = $intNew + 1 )
|
|
{
|
|
$strBinary = "0" . $strBinary;
|
|
}
|
|
|
|
return $strBinary;
|
|
}
|
|
static protected function PadBinary( $strBinary )
|
|
{
|
|
$intLen = \strlen( $strBinary );
|
|
$strBinary = $strBinary . "1";
|
|
|
|
for( $intPos = \strlen( $strBinary ) ; $intPos <= 447 ; $intPos = $intPos + 1 )
|
|
{
|
|
$strBinary = $strBinary . "0";
|
|
}
|
|
|
|
$strTemp = self::IntToBinary( $intLen );
|
|
|
|
for( $intPos = \strlen( $strTemp ) ; $intPos <= 63 ; $intPos = $intPos + 1 )
|
|
{
|
|
$strTemp = "0" . $strTemp;
|
|
}
|
|
|
|
return $strBinary . $strTemp;
|
|
}
|
|
static protected function BlockToHex( $strBinary )
|
|
{
|
|
$strHex = '';
|
|
for( $intPos = 1 ; $intPos <= \strlen( $strBinary ) ; $intPos = $intPos + 4 )
|
|
{
|
|
$strHex = $strHex . self::BinaryToHex( substr( $strBinary, $intPos - 1, 4 ) );
|
|
}
|
|
return $strHex;
|
|
}
|
|
static protected function DigestHex( $strHex, $strH0, $strH1, $strH2, $strH3, $strH4 )
|
|
{
|
|
//Constant hex words are used for encryption, these can be any valid 8 digit hex value
|
|
$strK[ 0 ] = "5A827999";
|
|
$strK[ 1 ] = "6ED9EBA1";
|
|
$strK[ 2 ] = "8F1BBCDC";
|
|
$strK[ 3 ] = "CA62C1D6";
|
|
|
|
//Hex words are used in the encryption process, these can be any valid 8 digit hex value
|
|
$strH[ 0 ] = $strH0;
|
|
$strH[ 1 ] = $strH1;
|
|
$strH[ 2 ] = $strH2;
|
|
$strH[ 3 ] = $strH3;
|
|
$strH[ 4 ] = $strH4;
|
|
|
|
//divide the Hex block into 16 hex words
|
|
for( $intPos = 0 ; $intPos <= ( \strlen( $strHex ) / 8 ) - 1 ; $intPos = $intPos + 1 )
|
|
{
|
|
$strWords[ intval( $intPos ) ] = substr( $strHex, ( intval( $intPos ) * 8 ) + 1 - 1, 8 );
|
|
}
|
|
|
|
//encode the Hex words using the constants above
|
|
//innitialize 80 hex word positions
|
|
for( $intPos = 16 ; $intPos <= 79 ; $intPos = $intPos + 1 )
|
|
{
|
|
$strTemp = $strWords[ intval( $intPos ) - 3 ];
|
|
$strTemp1 = self::HexBlockToBinary( $strTemp );
|
|
$strTemp = $strWords[ intval( $intPos ) - 8 ];
|
|
$strTemp2 = self::HexBlockToBinary( $strTemp );
|
|
$strTemp = $strWords[ intval( $intPos ) - 14 ];
|
|
$strTemp3 = self::HexBlockToBinary( $strTemp );
|
|
$strTemp = $strWords[ intval( $intPos ) - 16 ];
|
|
$strTemp4 = self::HexBlockToBinary( $strTemp );
|
|
$strTemp = self::BinaryXOR( $strTemp1, $strTemp2 );
|
|
$strTemp = self::BinaryXOR( $strTemp, $strTemp3 );
|
|
$strTemp = self::BinaryXOR( $strTemp, $strTemp4 );
|
|
$strWords[ intval( $intPos ) ] = self::BlockToHex( self::BinaryShift( $strTemp, 1 ) );
|
|
}
|
|
|
|
//initialize the changing word variables with the initial word variables
|
|
$strA[ 0 ] = $strH[ 0 ];
|
|
$strA[ 1 ] = $strH[ 1 ];
|
|
$strA[ 2 ] = $strH[ 2 ];
|
|
$strA[ 3 ] = $strH[ 3 ];
|
|
$strA[ 4 ] = $strH[ 4 ];
|
|
|
|
//Main encryption loop on all 80 hex word positions
|
|
for( $intPos = 0 ; $intPos <= 79 ; $intPos = $intPos + 1 )
|
|
{
|
|
$strTemp = self::BinaryShift( self::HexBlockToBinary( $strA[ 0 ] ), 5 );
|
|
$strTemp1 = self::HexBlockToBinary( $strA[ 3 ] );
|
|
$strTemp2 = self::HexBlockToBinary( $strWords[ intval( $intPos ) ] );
|
|
|
|
switch( $intPos )
|
|
{
|
|
case 0 :
|
|
case 1 :
|
|
case 2 :
|
|
case 3 :
|
|
case 4 :
|
|
case 5 :
|
|
case 6 :
|
|
case 7 :
|
|
case 8 :
|
|
case 9 :
|
|
case 10 :
|
|
case 11 :
|
|
case 12 :
|
|
case 13 :
|
|
case 14 :
|
|
case 15 :
|
|
case 16 :
|
|
case 17 :
|
|
case 18 :
|
|
case 19 :
|
|
$strTemp3 = self::HexBlockToBinary( $strK[ 0 ] );
|
|
$strTemp4 = self::BinaryOR( self::BinaryAND( self::HexBlockToBinary( $strA[ 1 ] ), self::HexBlockToBinary( $strA[ 2 ] ) ), self::BinaryAND( self::BinaryNOT( self::HexBlockToBinary( $strA[ 1 ] ) ), self::HexBlockToBinary( $strA[ 3 ] ) ) );
|
|
break;
|
|
case 20 :
|
|
case 21 :
|
|
case 22 :
|
|
case 23 :
|
|
case 24 :
|
|
case 25 :
|
|
case 26 :
|
|
case 27 :
|
|
case 28 :
|
|
case 29 :
|
|
case 30 :
|
|
case 31 :
|
|
case 32 :
|
|
case 33 :
|
|
case 34 :
|
|
case 35 :
|
|
case 36 :
|
|
case 37 :
|
|
case 38 :
|
|
case 39 :
|
|
$strTemp3 = self::HexBlockToBinary( $strK[ 1 ] );
|
|
$strTemp4 = self::BinaryXOR( self::BinaryXOR( self::HexBlockToBinary( $strA[ 1 ] ), self::HexBlockToBinary( $strA[ 2 ] ) ), self::HexBlockToBinary( $strA[ 3 ] ) );
|
|
break;
|
|
case 40 :
|
|
case 41 :
|
|
case 42 :
|
|
case 43 :
|
|
case 44 :
|
|
case 45 :
|
|
case 46 :
|
|
case 47 :
|
|
case 48 :
|
|
case 49 :
|
|
case 50 :
|
|
case 51 :
|
|
case 52 :
|
|
case 53 :
|
|
case 54 :
|
|
case 55 :
|
|
case 56 :
|
|
case 57 :
|
|
case 58 :
|
|
case 59 :
|
|
$strTemp3 = self::HexBlockToBinary( $strK[ 2 ] );
|
|
$strTemp4 = self::BinaryOR( self::BinaryOR( self::BinaryAND( self::HexBlockToBinary( $strA[ 1 ] ), self::HexBlockToBinary( $strA[ 2 ] ) ), self::BinaryAND( self::HexBlockToBinary( $strA[ 1 ] ), self::HexBlockToBinary( $strA[ 3 ] ) ) ), self::BinaryAND( self::HexBlockToBinary( $strA[ 2 ] ), self::HexBlockToBinary( $strA[ 3 ] ) ) );
|
|
break;
|
|
case 60 :
|
|
case 61 :
|
|
case 62 :
|
|
case 63 :
|
|
case 64 :
|
|
case 65 :
|
|
case 66 :
|
|
case 67 :
|
|
case 68 :
|
|
case 69 :
|
|
case 70 :
|
|
case 71 :
|
|
case 72 :
|
|
case 73 :
|
|
case 74 :
|
|
case 75 :
|
|
case 76 :
|
|
case 77 :
|
|
case 78 :
|
|
case 79 :
|
|
$strTemp3 = self::HexBlockToBinary( $strK[ 3 ] );
|
|
$strTemp4 = self::BinaryXOR( self::BinaryXOR( self::HexBlockToBinary( $strA[ 1 ] ), self::HexBlockToBinary( $strA[ 2 ] ) ), self::HexBlockToBinary( $strA[ 3 ] ) );
|
|
break;
|
|
}
|
|
|
|
$strTemp = self::BlockToHex( $strTemp );
|
|
$strTemp1 = self::BlockToHex( $strTemp1 );
|
|
$strTemp2 = self::BlockToHex( $strTemp2 );
|
|
$strTemp3 = self::BlockToHex( $strTemp3 );
|
|
$strTemp4 = self::BlockToHex( $strTemp4 );
|
|
|
|
$strTemp = self::HexAdd( $strTemp, $strTemp1 );
|
|
$strTemp = self::HexAdd( $strTemp, $strTemp2 );
|
|
$strTemp = self::HexAdd( $strTemp, $strTemp3 );
|
|
$strTemp = self::HexAdd( $strTemp, $strTemp4 );
|
|
|
|
$strA[ 4 ] = $strA[ 3 ];
|
|
$strA[ 3 ] = $strA[ 2 ];
|
|
$strA[ 2 ] = self::BlockToHex( self::BinaryShift( self::HexBlockToBinary( $strA[ 1 ] ), 30 ) );
|
|
$strA[ 1 ] = $strA[ 0 ];
|
|
$strA[ 0 ] = $strTemp;
|
|
}
|
|
|
|
//Concatenate the final Hex Digest
|
|
return $strA[ 0 ] . $strA[ 1 ] . $strA[ 2 ] . $strA[ 3 ] . $strA[ 4 ];
|
|
}
|
|
static protected function HexAdd( $strHex1, $strHex2 )
|
|
{
|
|
$n1 = hexdec( $strHex1 );
|
|
$n2 = hexdec( $strHex2 );
|
|
$sum = $n1 + $n2;
|
|
return sprintf( "%08X", $sum );
|
|
}
|
|
static protected function BinaryShift( $strBinary, $intPos )
|
|
{
|
|
return substr( $strBinary, \strlen( $strBinary ) - ( \strlen( $strBinary ) - intval( $intPos ) ) ) . substr( $strBinary, 0, intval( $intPos ) );
|
|
}
|
|
|
|
// Function performs an exclusive or function on each position of two binary values
|
|
static protected function BinaryXOR( $strBin1, $strBin2 )
|
|
{
|
|
$strBinaryFinal = '';
|
|
for( $intPos = 1 ; $intPos <= \strlen( $strBin1 ) ; $intPos = $intPos + 1 )
|
|
{
|
|
switch( substr( $strBin1, intval( $intPos ) - 1, 1 ) )
|
|
{
|
|
case substr( $strBin2, intval( $intPos ) - 1, 1 ) :
|
|
$strBinaryFinal = $strBinaryFinal . "0";
|
|
break;
|
|
default :
|
|
$strBinaryFinal = $strBinaryFinal . "1";
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $strBinaryFinal;
|
|
}
|
|
|
|
// Function performs an inclusive or function on each position of two binary values
|
|
static protected function BinaryOR( $strBin1, $strBin2 )
|
|
{
|
|
$strBinaryFinal = '';
|
|
for( $intPos = 1 ; $intPos <= \strlen( $strBin1 ) ; $intPos = $intPos + 1 )
|
|
{
|
|
if ( substr( $strBin1, intval( $intPos ) - 1, 1 ) == "1" || substr( $strBin2, intval( $intPos ) - 1, 1 ) == "1" )
|
|
{
|
|
$strBinaryFinal = $strBinaryFinal . "1";
|
|
}
|
|
else
|
|
{
|
|
$strBinaryFinal = $strBinaryFinal . "0";
|
|
}
|
|
}
|
|
|
|
return $strBinaryFinal;
|
|
}
|
|
|
|
// Function performs an AND function on each position of two binary values
|
|
static protected function BinaryAND( $strBin1, $strBin2 )
|
|
{
|
|
$strBinaryFinal = '';
|
|
for( $intPos = 1 ; $intPos <= \strlen( $strBin1 ) ; $intPos = $intPos + 1 )
|
|
{
|
|
if ( substr( $strBin1, intval( $intPos ) - 1, 1 ) == "1" && substr( $strBin2, intval( $intPos ) - 1, 1 ) == "1" )
|
|
{
|
|
$strBinaryFinal = $strBinaryFinal . "1";
|
|
}
|
|
else
|
|
{
|
|
$strBinaryFinal = $strBinaryFinal . "0";
|
|
}
|
|
}
|
|
|
|
return $strBinaryFinal;
|
|
}
|
|
|
|
// Function makes each position of a binary value from 1 to 0 and 0 to 1
|
|
static protected function BinaryNOT( $strBinary )
|
|
{
|
|
$strBinaryFinal = '';
|
|
for( $intPos = 1 ; $intPos <= \strlen( $strBinary ) ; $intPos = $intPos + 1 )
|
|
{
|
|
if ( substr( $strBinary, intval( $intPos ) - 1, 1 ) == "1" )
|
|
{
|
|
$strBinaryFinal = $strBinaryFinal . "0";
|
|
}
|
|
else
|
|
{
|
|
$strBinaryFinal = $strBinaryFinal . "1";
|
|
}
|
|
}
|
|
|
|
return $strBinaryFinal;
|
|
}
|
|
|
|
// Function Converts a 8 digit/32 bit hex value to its 32 bit binary equivalent
|
|
static protected function HexBlockToBinary( $strHex )
|
|
{
|
|
$strTemp = '';
|
|
for( $intPos = 1 ; $intPos <= \strlen( $strHex ) ; $intPos = $intPos + 1 )
|
|
{
|
|
$strTemp = $strTemp . self::HexToBinary( substr( $strHex, intval( $intPos ) - 1, 1 ) );
|
|
}
|
|
|
|
return $strTemp;
|
|
}
|
|
} |