Add TOTP.php
This commit is contained in:
parent
b22ae5e702
commit
3098126d3a
67
TOTP.php
Normal file
67
TOTP.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
readonly class TOTP
|
||||
{
|
||||
private const BASE32CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; // RFC 4648 Base32
|
||||
|
||||
public function __construct(private string $secret) {}
|
||||
|
||||
public static function otpSecret($uniqueString, $length = 20): string
|
||||
{
|
||||
// Уникальная строка + текущее время в микросекундах
|
||||
$seed = $uniqueString . microtime(true);
|
||||
|
||||
// Хэшируем сид для более случайного распределения
|
||||
$seed = hash('sha256', $seed);
|
||||
|
||||
$secret = '';
|
||||
$max = strlen(self::BASE32CHARS) - 1;
|
||||
|
||||
// Генерируем случайную строку нужной длины
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$index = hexdec(substr($seed, $i, 2)) % $max; // Получаем индекс символа из хэшированного сида
|
||||
$secret .= self::BASE32CHARS[$index];
|
||||
}
|
||||
|
||||
return $secret;
|
||||
}
|
||||
|
||||
public function generate(int $digits = 6, int $timeStep = 30): string
|
||||
{
|
||||
$time = floor(time() / $timeStep);
|
||||
$secretKey = $this->base32_decode($this->secret);
|
||||
$binaryTime = pack('N*', 0) . pack('N*', $time);
|
||||
$hash = hash_hmac('sha1', $binaryTime, $secretKey, true);
|
||||
$offset = ord($hash[strlen($hash) - 1]) & 0x0F;
|
||||
$otp = (
|
||||
((ord($hash[$offset]) & 0x7F) << 24) |
|
||||
((ord($hash[$offset + 1]) & 0xFF) << 16) |
|
||||
((ord($hash[$offset + 2]) & 0xFF) << 8) |
|
||||
(ord($hash[$offset + 3]) & 0xFF)
|
||||
) % 10 ** $digits;
|
||||
return str_pad((string)$otp, $digits, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
private function base32_decode(string $input): string
|
||||
{
|
||||
$base32charsFlipped = array_flip(str_split(self::BASE32CHARS));
|
||||
$output = '';
|
||||
$v = 0;
|
||||
$vbits = 0;
|
||||
|
||||
for ($i = 0, $j = strlen($input); $i < $j; $i++) {
|
||||
$v <<= 5;
|
||||
if ($input[$i] == '=') continue;
|
||||
$v += $base32charsFlipped[$input[$i]];
|
||||
$vbits += 5;
|
||||
|
||||
if ($vbits >= 8) {
|
||||
$vbits -= 8;
|
||||
$output .= chr(($v & (0xFF << $vbits)) >> $vbits);
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user