<?php
/*
 * A PHP implementation of the Secure Hash Algorithm, SHA-1, as defined
 * in FIPS PUB 180-1
 * Adjusted from the Javascript implementation by Joror (daan@parse.nl).
 *
 * Javascript Version 2.1 Copyright Paul Johnston 2000 - 2002.
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for details.
 */

class Sha1Lib {
	/*
	 * Configurable variables. You may need to tweak these to be compatible with
	 * the server-side, but the defaults work in most cases.
	 */
	var $hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase      */
	var $b64pad  =''; /* base-64 pad character. "=" for strict RFC compliance */
	var $chrsz   = 8; /* bits per input character. 8 - ASCII; 16 - Unicode    */

	/*
	 * These are the functions you'll usually want to call
	 * They take string arguments and return either hex or base-64 encoded strings
	 */
	function hex_sha1($s){return $this->binb2hex($this->core_sha1($this->str2binb($s),strlen($s) * $this->chrsz));}
	function b64_sha1($s){return $this->binb2b64($this->core_sha1($this->str2binb($s),strlen($s) * $this->chrsz));}
	function str_sha1($s){return $this->binb2str($this->core_sha1($this->str2binb($s),strlen($s) * $this->chrsz));}
	function hex_hmac_sha1($key, $data){ return $this->binb2hex($this->core_hmac_sha1($key, $data));}
	function b64_hmac_sha1($key, $data){ return $this->binb2b64($this->core_hmac_sha1($key, $data));}
	function str_hmac_sha1($key, $data){ return $this->binb2str($this->core_hmac_sha1($key, $data));}

	/*
	 * Perform a simple self-test to see if the VM is working
	 */
	function sha1_vm_test() {
		return $this->hex_sha1('abc') == 'a9993e364706816aba3e25717850c26c9cd0d89d';
	}

	/*
	 * Calculate the SHA-1 of an array of big-endian words, and a bit $length
	 */
	function core_sha1($x, $len) {
		/* append padding */
		$x[$len >> 5] |= 0x80 << (24 - $len % 32);
		$x[(($len + 64 >> 9) << 4) + 15] = $len;

		$w = Array();
		$a =  1732584193;
		$b = -271733879;
		$c = -1732584194;
		$d =  271733878;
		$e = -1009589776;

		for ($i=0,$count=sizeof($x);$i<$count;$i+=16) {
			$olda = $a;
			$oldb = $b;
			$oldc = $c;
			$oldd = $d;
			$olde = $e;

			for($j=0; $j < 80; $j++) {
				$w[$j]=($j<16)?$x[$i + $j]:($this->rol($w[$j-3]^$w[$j-8]^$w[$j-14]^$w[$j-16], 1));

				$t=$this->safe_add($this->safe_add($this->rol($a, 5), $this->sha1_ft($j, $b, $c, $d)),									 $this->safe_add($this->safe_add($e, $w[$j]), $this->sha1_kt($j)));
				$e = $d;
				$d = $c;
				$c = $this->rol($b, 30);
				$b = $a;
				$a = $t;
			}

			$a = $this->safe_add($a, $olda);
			$b = $this->safe_add($b, $oldb);
			$c = $this->safe_add($c, $oldc);
			$d = $this->safe_add($d, $oldd);
			$e = $this->safe_add($e, $olde);
		}

		return Array($a, $b, $c, $d, $e);
	}

	/*
	 * Joror: PHP does not have the java(script) >>> operator, so this is a 
	 * replacement function. Credits to Terium.
	 */
	function zerofill_rightshift($a, $b) { 
		$z = hexdec(80000000); 
		if ($z & $a) {
			$a >>= 1;
			$a &= (~ $z);
			$a |= 0x40000000;
			$a >>= ($b-1);
		} else {
			$a >>= $b;
		}
		return $a;
	}

	/*
	 * Perform the appropriate triplet combination function for the current
	 * iteration
	 */
	function sha1_ft($t, $b, $c, $d) {
		if($t < 20) return ($b & $c) | ((~$b) & $d);
		if($t < 40) return $b ^ $c ^ $d;
		if($t < 60) return ($b & $c) | ($b & $d) | ($c & $d);
		return $b ^ $c ^ $d;
	}

	/*
	 * Determine the appropriate additive constant for the current iteration
	 * Silly php does not understand the inline-if operator well when nested,
	 * so that's why it's ()ed now.
	 */
	function sha1_kt($t) {
		return ($t < 20) ?	1518500249 : (($t < 40) ?	1859775393 :
				(($t < 60) ? -1894007588 : -899497514));
	}

	/*
	 * Calculate the HMAC-SHA1 of a key and some data
	 */
	function core_hmac_sha1($key, $data) {
		$bkey = $this->str2binb($key);
		if (sizeof($bkey) > 16)
			$bkey = $this->core_sha1($bkey, sizeof($key) * $this->chrsz);

		for($i = 0; $i < 16; $i++) {
			$ipad[$i] = $bkey[$i] ^ 0x36363636;
			$opad[$i] = $bkey[$i] ^ 0x5C5C5C5C;
		}

		return $this->core_sha1(array_merge($opad,$this->core_sha1(array_merge($ipad,$this->str2binb($data)), 512 + sizeof($data) * $this->chrsz)), 512 + 160);
	}

	/*
	 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
	 * to work around bugs in some JS interpreters.
	 */
	function safe_add($x, $y) {
		return (($x >> 16) + ($y >> 16) + ($lsw >> 16) << 16) | (($x & 0xFFFF) + ($y & 0xFFFF) & 0xFFFF);
	}

	/*
	 * Bitwise rotate a 32-bit number to the left.
	 */
	function rol($num, $cnt) {
		return ($num << $cnt) | $this->zerofill_rightshift($num, (32 - $cnt));
	}

	/*
	 * Convert an 8-bit or 16-bit string to an array of big-endian words
	 * In 8-bit function, characters >255 have their hi-byte silently ignored.
	 */
	function str2binb($str) {
		$bin = Array();
		$mask = (1 << $this->chrsz) - 1;
		for($i=0, $len=strlen($str) * $this->chrsz; $i < $len; $i += $this->chrsz)
			$bin[$i >> 5] |= (ord($str{$i / $this->chrsz}) & $mask) << (24 - $i%32);

		return $bin;
	}

	/*
	 * Convert an array of big-endian words to a string
	 */
	function binb2str($bin) {
		$str = '';
		$mask = (1 << $this->chrsz) - 1;
		for($i=0, $len=sizeof($bin)*32; $i<$len; $i+=$this->chrsz)
			$str .= chr($this->zerofill_rightshift($bin[$i>>5], 24 - $i%32) & $mask);
		return $str;
	}

	/*
	 * Convert an array of big-endian words to a hex string.
	 */
	function binb2hex($binarray) {
		$hex_tab = $this->hexcase ? '0123456789ABCDEF':'0123456789abcdef';
		$str = '';
		for($i=0, $len=sizeof($binarray)*4; $i<$len; $i++)
			$str .= $hex_tab{($binarray[$i>>2] >> ((3 - $i%4)*8+4)) & 0xF} .
					$hex_tab{($binarray[$i>>2] >> ((3 - $i%4)*8 )) & 0xF};

		return $str;
	}

	/*
	 * Convert an array of big-endian words to a base-64 string
	 */
	function binb2b64($binarray) {
		$tab='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
		$str='';
		for($i=0, $len=sizeof($binarray)*4; i<$len; $i+=3) {
			$triplet = ((($binarray[$i   >> 2] >> 8 * (3 -  $i   %4)) & 0xFF) << 16)
							 | ((($binarray[$i+1 >> 2] >> 8 * (3 - ($i+1)%4)) & 0xFF) << 8 )
							 |  (($binarray[$i+2 >> 2] >> 8 * (3 - ($i+2)%4)) & 0xFF);
			for($j=0; $j<4; $j++) {
				if ($i*8+$j*6 > sizeof($binarray)*32) $str .= $this->b64pad;
				else $str .= $tab{($triplet >> 6*(3-j)) & 0x3F};
			}
		}
		return $str;
	}
}

if ( !function_exists('sha1') ) {
	function sha1($string, $raw_output=false ) {
		$library = new Sha1Lib();
		return $raw_output ? $library->str_sha1($string) : $library->hex_sha1($string);
	}
}
?>