<?php
/*
User Encryption class. Used to encrypt and hash user information (passwords etc). 

Assuming username is an email address and password adhears to reg ex and is less than 20 characters long. 
And time is a standard UNIX timestamp.

Loganc June 2014

Updated Feb 2019 to extend the EncryptionCore class to handle backwards compatible mcrypt method 
calls php7 onwards
*/

class UserEncryption {
			
	protected $glue = "|";
	protected $prefix;
    protected $cost = '$10$';
    protected $cipher = 'aes-128-gcm';
    protected $iv;
    protected $tagsize = 16;
    protected $ivsize;
    protected $encryption_key;
    
    
	var $username_filter = "^[a-z0-9\._-]+@+[a-z0-9\._-]+\.+[a-z]{2,4}$";
	var $username_length = 50;
	var $password_filter = "^[a-zA-Z0-9\$\+-]+$";
	var $password_length = 20;
	var $time_filter = "^[0-9]{10,12}$";
	
	public function __construct ($encryption_key) {
		
		global $LOG;
		$this->prefix = (version_compare (PHP_VERSION, '5.3.7') < 0) ? '$2a' : '$2y';
        $this->encryption_key = $encryption_key;  
        $ivlen = openssl_cipher_iv_length($this->cipher);
    	$this->iv = openssl_random_pseudo_bytes($ivlen);
      
		return true;
	}
	
	function Package($username, $password) {
		
		$time = time();
		$data = array($time, $username, $password);
		$buffer = implode($this->glue, $data);
		return $this->_Encrypt($buffer);
	}
	
	function Unpackage($package) {
		
		$data = array();
		$buffer = $this->_Decrypt($package);

		list($data['time'], $data['username'], $data['password']) = explode($this->glue, $buffer);
		
		// Sanity check and validate decrypted user data.
		if(!$this->_CheckUserTime($data['time'])) {
			$LOG->LogError("User encryption, invalid user time string", E_USER_WARNING);
			return false;
		}
		if(!$this->_CheckUsername($data['username'])) {
			$LOG->LogError("User encryption, invalid username string", E_USER_WARNING);
			return false;
		}
		if(!$this->_CheckPassword($data['password'])) {
			$LOG->LogError("User encryption, invalid password string", E_USER_WARNING);
			return false;
		}
		return $data;
	}
	
 	public function _Encrypt($str) {
    	// Encrypt the data using AES 256 encryption in gcm mode using our encryption key and initialization vector.
    	$encrypted = openssl_encrypt($str, $this->cipher, $this->encryption_key, $options=0, $this->iv, $tag);

        // Return encrypted value and list of encryption hash array keys
        return base64_encode($this->iv . $tag . $encrypted);
    }
   
	public function _Decrypt($encoded_package) {
			
		$package = base64_decode($encoded_package);
		$ivsize = openssl_cipher_iv_length($this->cipher);
        $iv = substr($package, 0, $ivsize);
        $tag = substr($package, $ivsize, $this->tagsize);
        $crypt_text = substr($package, ($ivsize + $this->tagsize));

		if(!empty ($crypt_text) && !empty ($iv)) {
			if($decrypted = openssl_decrypt($crypt_text, $this->cipher, $this->encryption_key, $options=0, $iv, $tag)) {
         		return $decrypted;
            }            
        }
        return false;
    }

	function _HashPassword($password, $salt) {	
		return sha1($password . $salt);
	}
	
	function _CheckUsername($username) {
		if(preg_match('/' . $this->username_filter . '/', $username) && strlen($username) < $this->username_length) {
			return true;
		}
		return false;
	}
	
	function _CheckPassword($password) {
		if(preg_match("/" . $this->password_filter . "/", $password) && strlen($password) < $this->password_length) {
			return true;
		}
		return false;
	}
	
	function _CheckUserTime($user_time) {

		if(preg_match("/" . $this->time_filter . "/", $user_time)) {
			return true;
		}
		return false;
	}
}
?>
