<?php

define("FNV_INIT", 2166136261);//fnv Initial const
/*************************************************************
* class :secu_hmac_fnv
* remark:secu_hmac_fnv hash function class
*  			--实现hmac_fnv哈希函数
************************************************************/
class secu_hmac_fnv
{
		function fnv_32a_buf($text, $init)
		{
			$hval = $init;
			$text_len = strlen($text);
			$buf = str_split($text);
			for($i = 0; $i < $text_len; ++$i)
			{
					$hval ^= ord($buf[$i]);
					$hval += ($hval<<1)+($hval<<4)+($hval<<7)+($hval<<8)+($hval<<24);
			}
			$tmp = sprintf("%x", $hval);
			$hval = hexdec($tmp);
			return $hval;
		}
		function fnv_32arr_buf($text, $init)
		{
			$hval = $init;
			$text_len = count($text);
			
			for($i = 0; $i < $text_len; ++$i)
			{
					$hval ^= $text[$i];
					$hval += ($hval<<1)+($hval<<4)+($hval<<7)+($hval<<8)+($hval<<24);
			}
			
			$tmp = sprintf("%x", $hval);
			$hval = hexdec($tmp);
			return $hval;
		}
		function secu_encrypt($share, $text, $pwlen=6)
		{
			$ret_val = $this->fnv_32arr_buf($text, $this->fnv_32a_buf($share, FNV_INIT));
			
			$ex = 1;
			for($i = 0; $i < $pwlen; ++$i)
			{
				$ex *= 10;
			}
			$ret_val = bcmod($ret_val, $ex);
			$preadd = array(
			"", "0", "00", "000", "0000", "00000", 
			"000000", "0000000", "00000000", "000000000"
			);
			
			return $preadd[$pwlen - strlen($ret_val)].$ret_val;
		}
}

/*********************************************************
* function:counter加1操作
* inparams:&$hcounter, &$lcounter
* outparams:&$hcounter, &$lcounter
*remark:实现counter的加1操作
********************************************************/
function counter_self_add(&$hcounter, &$lcounter)
{
	if(0xffffffff == $lcounter)
	{
		if(0xffffffff == $hcounter)
		{
			$hcounter = 0;
		}
		else
		{
			++$hcounter;
		}
		$lcounter = 0;
	}
	else
	{
		++$lcounter;
	}
}
/************************************************************
* function:countertoarr
* inparams:$hcounter, $lcounter
* outparams:none
* return: array
* remark: 将counter($hcounter|$lcounter)放进数组中(按字节取值)
*************************************************************/
function countertoarr($hcounter, $lcounter)
{
	 $ct_tmp[0] = (int)($hcounter/0x1000000);
	 $hcounter = bcmod($hcounter, 0x1000000);
	 $ct_tmp[1] = (int)($hcounter/0x10000);
	 $hcounter = bcmod($hcounter, 0x10000);
	 $ct_tmp[2] = (int)($hcounter/0x100);
	 $hcounter = bcmod($hcounter, 0x100);
	 $ct_tmp[3] = $hcounter;
	 
	 $ct_tmp[4] = (int)($lcounter/0x1000000);
	 $lcounter = bcmod($lcounter, 0x1000000);
	 $ct_tmp[5] = (int)($lcounter/0x10000);
	 $lcounter = bcmod($lcounter, 0x10000);
	 $ct_tmp[6] = (int)($lcounter/0x100);
	 $lcounter = bcmod($lcounter, 0x100);
	 $ct_tmp[7] = $lcounter;
	 return $ct_tmp;
}


/***********************************************************
* HOTP Class
***********************************************************/
class secu_hotp
{
	function generateotp($key, $data, $diglen)
	{
		if(strlen($key) > 64)
		{
			$key = pack("H*", sha1($key));
		}
		$key = str_pad($key, 64, chr(0x00));
		$ipad = str_pad("", 64, chr(0x36));
		$opad = str_pad("", 64, chr(0x5c));
		$k_ipad = $key ^ $ipad;
		$k_opad = $key ^ $opad;
		$rettmp = sha1($k_ipad.$data);
		$rettmp = pack("H*", $rettmp);
		$rettmp = sha1($k_opad.$rettmp);
		$rettmp = pack("H*", $rettmp);
		$retarr = str_split($rettmp);
		$offset =  ord($retarr[19])&0x0f;
		$rettmp = ((ord($retarr[$offset]) & 0x7f) << 24)
							| ((ord($retarr[$offset+1]) & 0xff) << 16)
							| ((ord($retarr[$offset+2]) & 0xff) << 8)
							| (ord($retarr[$offset+3]) & 0xff);
		$div = array(10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000);
		$retval = bcmod($rettmp, $div[$diglen-1]);
		return str_pad($retval, $diglen, "0", STR_PAD_LEFT);
	}
	function generateotp2($key, $hcounter, $lcounter, $diglen)
	{
		$data = countertoarr($hcounter, $lcounter);
		$counter = "";
		foreach($data as $val)
		{
			$counter .= chr($val);
		}
		return $this->generateotp($key, $counter, $diglen);
	}
}
/*********************************************************************************
* function:secu_uservalid_check
* inparams:$user_otp --用户动态口令
* 				 $share 	--共享密钥
*					 $hcounter, $lcounter --counter
*					 $wnd --认证窗口 (default 30)
* return : -1 -- 认证失败 ;>=0 counter 增量
* remark :实现用户身份认证
********************************************************************************/
function secu_uservalid_check($user_otp, $share, $hcounter, $lcounter, $diglen=6, $wnd=30)
{
	//$hmac_fnv = new secu_hmac_fnv();//generate a secu_hmac_fnv obj
	$encryobj = new secu_hotp();
	for($i = 0; $i < $wnd; ++$i)
	{
		//$text = countertoarr($hcounter, $lcounter);
		if($user_otp === $encryobj->generateotp2($share, $hcounter, $lcounter, $diglen))
		{
			return $i+1;
		}
		counter_self_add($hcounter, $lcounter);
	}
	return -1;
}

function secu_userauth($user_otp, $share, &$hcounter, &$lcounter, $authtype=2, $spin="", $diglen=6, $wnd=30)
{
	//$hmac_fnv = new secu_hmac_fnv();//generate a secu_hmac_fnv obj
	$encryobj = new secu_hotp();
	for($i = 0; $i < $wnd; ++$i)
	{
		$tmp_otp = $encryobj->generateotp2($share, $hcounter, $lcounter, $diglen);
		if(2 === intval($authtype))
		{
			$tmp_otp = $tmp_otp.$spin;
		}
		counter_self_add($hcounter, $lcounter);
		if($user_otp === $tmp_otp)
		{
			return $i+1;
		}
	}
	return -1;
}
/*************************************************************************************
* function:phpsrv_user_synchronize
* inparams:$user_otp_pre, $user_otp_snd --两个连续的动态口令
*					 $share   -- 共享密钥
*					 $hcounter, $lcounter -- counter
*					 $wnd=150 --同步窗口
*	return :int -1--同步失败 ;>=0 --counter增量
* remark: 实现用户token同步
*************************************************************************************/
function secu_user_synchronize($user_otp_pre, $user_otp_snd, $share, &$hcounter, &$lcounter, $diglen=6, $wnd=150)
{
	//$hmac_fnv = new secu_hmac_fnv();//generate a secu_hmac_fnv obj
	$encryobj = new secu_hotp();
	$ret1 = $encryobj->generateotp2($share, $hcounter, $lcounter, $diglen);
	for($i = 0; $i < $wnd; ++$i)
	{
		counter_self_add($hcounter, $lcounter);
		$ret2 = $encryobj->generateotp2($share, $hcounter, $lcounter, $diglen);
		if(($user_otp_pre == $ret1) && ($user_otp_snd == $ret2))//同步成功
		{
			counter_self_add($hcounter, $lcounter);	
			return $i+2;
		}
		$ret1 = $ret2;
	}
	return -1;
}

?>