Skip to content

Instantly share code, notes, and snippets.

@jamesliu96
Created May 18, 2025 17:55
Show Gist options
  • Save jamesliu96/9de800a55e8abea42f84e208971b6579 to your computer and use it in GitHub Desktop.
Save jamesliu96/9de800a55e8abea42f84e208971b6579 to your computer and use it in GitHub Desktop.
<?php
define('OTP_LEN', 6);
define('OTP_INT', 30);
define('OTP_WIN', 4);
function hotp(string $key, int $ctr, int $len = OTP_LEN)
{
$result = [];
while ($ctr !== 0) {
$result[] = chr($ctr & 0xFF);
$ctr >>= 8;
}
$binCounter = str_pad(implode('', array_reverse($result)), 8, "\000", STR_PAD_LEFT);
$hash = hash_hmac('sha1', $binCounter, (function (string $input) {
$input = strtoupper($input);
$input = str_replace('=', '', $input);
$input = str_split($input);
$binaryString = '';
$count = count($input);
for ($i = 0; $i < $count; $i += 8) {
$x = '';
$x .= (function (int $i, array $input) {
static $flippedMap = [
'A' => '0',
'B' => '1',
'C' => '2',
'D' => '3',
'E' => '4',
'F' => '5',
'G' => '6',
'H' => '7',
'I' => '8',
'J' => '9',
'K' => '10',
'L' => '11',
'M' => '12',
'N' => '13',
'O' => '14',
'P' => '15',
'Q' => '16',
'R' => '17',
'S' => '18',
'T' => '19',
'U' => '20',
'V' => '21',
'W' => '22',
'X' => '23',
'Y' => '24',
'Z' => '25',
'2' => '26',
'3' => '27',
'4' => '28',
'5' => '29',
'6' => '30',
'7' => '31'
];
$x = '';
for ($j = 0; $j < 8; $j++) {
if (!isset($input[$i + $j])) continue;
$x .= str_pad(base_convert($flippedMap[$input[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT);
}
return $x;
})($i, $input);
$eightBits = str_split($x, 8);
$bitCount = count($eightBits);
$binaryString .= (function (int $bitCount, array $eightBits) {
$binaryString = '';
for ($z = 0; $z < $bitCount; $z++)
$binaryString .= (($y = chr((int)base_convert($eightBits[$z], 2, 10))) || ord($y) === 48) ? $y : '';
return $binaryString;
})($bitCount, $eightBits);
}
return rtrim($binaryString, "\0");
})($key), true);
$unpacked = unpack('C*', $hash);
$hmac = array_values($unpacked);
$offset = ($hmac[count($hmac) - 1] & 0xF);
$code = ($hmac[$offset] & 0x7F) << 24 | ($hmac[$offset + 1] & 0xFF) << 16 | ($hmac[$offset + 2] & 0xFF) << 8 | ($hmac[$offset + 3] & 0xFF);
$otp = $code % (10 ** $len);
return str_pad((string)$otp, $len, '0', STR_PAD_LEFT);
}
function hotp_verify(string $seed, int $ctr, string $key, int $len = OTP_LEN)
{
return hotp($seed, $ctr, $len) === $key;
}
function totp_time(int $int = OTP_INT)
{
return (int)floor(time() / $int);
}
function totp(string $key, int $int = OTP_INT, int $len = OTP_LEN)
{
return hotp($key, totp_time($int), $len);
}
function totp_verify(string $seed, int $timestamp, string $key, int $len = OTP_LEN, int $win = OTP_WIN)
{
for ($ts = $timestamp - $win; $ts <= $timestamp + $win; $ts++)
if (hotp_verify($seed, $ts, $key, $len)) return true;
return false;
}
function token_valid(string $name, string $token)
{
if (!isset($_GET[$name])) return false;
$t = $_GET[$name];
if (is_string($t) && ctype_digit($t) && strlen($t) === OTP_LEN && totp_verify($token, totp_time(), $t))
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment