class HOTP {
private $secret = '123456';
private $window = 3;
private $counter = 0;
public function __construct($secret = "123456", $window = 3, $counter = 0) {
$this->secret = empty($secret) ? "123456" : $secret;
$this->window = $window < 0 || $window > 100 ? 50 : $window;
$this->counter = $counter;
}
public function gen($counter = null) {
if ($counter === null) {
if ($this->counter === null) {
$this->counter = 0;
}
$counter = $this->counter;
$ret = $this->gen($counter);
$this->counter += 1;
return $ret;
}
$dataStr = $this->longToString($counter);
$bytes = $this->hmacSHA1Encrypt($dataStr);
$num = $this->convert($bytes);
$num = $num % 1000000;
return sprintf("%06d", $num);
}
public function verify($token, $counter = null) {
if ($counter === null) {
if ($this->counter === null) {
$this->counter = 0;
}
$counter = $this->counter;
return $this->verify($token, $counter);
}
for ($i = 0; $i < $this->window; $i++) {
{
$tcounter = $counter - $i;
if ($token === $this->gen($tcounter))
return $i;
}
if ($i === 0)
continue;
{
$tcounter = $counter + $i;
if ($token === $this->gen($tcounter))
return $i;
}
}
return null;
}
private function hmacSHA1Encrypt($dataStr) {
$secretStr = $this->secret;
$hash = hash_hmac('sha1', $dataStr, $secretStr);
$bytes = Array();
foreach(str_split($hash, 2) as $hex) {
$hmac[] = hexdec($hex);
}
return $hmac;
}
private function convert($bytes20) {
$offset = $bytes20[19] & 0xf;
$v = ($bytes20[$offset] & 0x7F) << 24 |
($bytes20[$offset + 1] & 0xFF) << 16 |
($bytes20[$offset + 2] & 0xFF) << 8 |
($bytes20[$offset + 3] & 0xFF);
return $v;
}
private function bytesToString($bytes) {
$str = '';
foreach($bytes as $ch) {
$str .= chr($ch);
}
return $str;
}
private function longToBytes($v) {
$bytes = Array();
$vlow = $v & 0xFFFFFFFF;
$vhigh = floor($v / 4294967296); // 没有long类型,使用除以2^32代替右移32位
$bytes[] = (($vhigh >> 56) & 0xFF);
$bytes[] = (($vhigh >> 48) & 0xFF);
$bytes[] = (($vhigh >> 40) & 0xFF);
$bytes[] = (($vhigh >> 32) & 0xFF);
$bytes[] = (($vlow >> 24) & 0xFF);
$bytes[] = (($vlow >> 16) & 0xFF);
$bytes[] = (($vlow >> 8) & 0xFF);
$bytes[] = (($vlow >> 0) & 0xFF);
return $bytes;
}
private function longToString($v) {
$bytes = $this->longToBytes($v);
$ret = $this->bytesToString($bytes);
return $ret;
}
}
class TOTP {
private $hotp;
private $interval = 30;
public function __construct($secret = "123456", $window = 3, $interval = 30) {
$this->hotp = new HOTP($secret, $window, 0);
$this->interval = $interval;
}
public function gen($timestamp = null) {
$timestamp = empty($timestamp) ? (time() * 1000) : $timestamp;
$timestamp_second = $timestamp / 1000;
$tcounter = floor($timestamp_second / $this->interval);
return $this->hotp->gen($tcounter);
}
public function gen2($timestamp = null) {
$timestamp = empty($timestamp) ? (time() * 1000) : $timestamp;
$timestamp_second = $timestamp / 1000;
$tcounter = floor($timestamp_second / $this->interval);
$token = $this->hotp->gen($tcounter);
$step = $timestamp_second % $this->interval;
$refresh_time = $tcounter * $this->interval;
return array("token" => $token,
"loop_length" => $this->interval,
"loop_step" => $step,
"refresh_interval" => $this->interval * 1000,
"refresh_time" => $refresh_time * 1000,
"timestamp" => $timestamp
);
}
public function verify($token) {
$now = time();
$tcounter = floor($now / $this->interval);
return $this->hotp->verify($token, $tcounter);
}
}
/**
echo "
";
$totp1 = new TOTP("hello", 3, 30);
echo json_encode($totp1->gen2(), JSON_PRETTY_PRINT);
echo $totp1->verify('975046');
echo "
";**/
一键复制
编辑
Web IDE
原始数据
按行查看
历史