<?php

namespace Vinteract\Encryption;

class Bcrypt {
	
	/**
	*  
	*  This class is based on the CodeIgniter library created
	*  by Waldir Bertazzi Junior (http://waldir.org/).
	*  
	**/
	
	private static $randomState;
	private static $times = 12;
	
	public static function hash($input) {
		$hash = crypt($input, self::generateSalt());
		if(strlen($hash) > 13)
			return $hash;
		return false;
	}
	
	public static function compare($input, $hashed_string) {
		$hash = crypt($input, $hashed_string);
		return $hash === $hashed_string;
	}
	
	public static function generateSalt() {
		$salt = sprintf('$2a$%02d$', self::$times);
		$bytes = self::getRandomBytes(16);
		$salt .= self::encodeBytes($bytes);
		return $salt;
	}
	
	public static function getRandomBytes($count) {
		
		$bytes = '';
		
		if(function_exists('openssl_random_pseudo_bytes') && !(PHP_OS == 'Windows' || PHP_OS == 'WIN32' || PHP_OS == 'WINNT')) {
			$bytes = openssl_random_pseudo_bytes($count);
		}
		
		if($bytes === '' && is_readable('/dev/urandom') && ($h_rand = @fopen('/dev/urandom', 'rb')) !== false){
			$bytes = fread($h_rand, $count);
			fclose($h_rand);
		}
		
		if(strlen($bytes) < $count) {
			
			$bytes = '';
			
			if(self::$randomState === null) {
				self::$randomState = microtime();
				if(function_exists('getmypid')) {
					self::$randomState .= getmypid();
				}
			}
			
			for($i = 0; $i < $count; $i += 16) {
				self::$randomState = md5(microtime() . self::randomState);
				if (PHP_VERSION >= '5') {
					$bytes .= md5(self::$randomState, true);
				} else {
					$bytes .= pack('H*', md5(self::$randomState));
				}
			}

			$bytes = substr($bytes, 0, $count);
			
		}
		
		return $bytes;
		
	}
	
	/**
	*  Function taken from PHP Password Hashing Framework
	*
	*  @return void
	*  @author PHP Password Hashing Framework
	*  @link http://www.openwall.com/phpass/
	**/
	
	public static function encodeBytes($input) {
		
		$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

		$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;
		
	}
	
}
