Update TOTP.php
PSR-1
This commit is contained in:
parent
3098126d3a
commit
1d70645f9e
49
TOTP.php
49
TOTP.php
@ -6,9 +6,21 @@ readonly class TOTP
|
|||||||
{
|
{
|
||||||
private const BASE32CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; // RFC 4648 Base32
|
private const BASE32CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; // RFC 4648 Base32
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Конструктор для инициализации объекта TOTP с секретным ключом.
|
||||||
|
*
|
||||||
|
* @param string $secret Секретный ключ в кодировке Base32.
|
||||||
|
*/
|
||||||
public function __construct(private string $secret) {}
|
public function __construct(private string $secret) {}
|
||||||
|
|
||||||
public static function otpSecret($uniqueString, $length = 20): string
|
/**
|
||||||
|
* Генерация случайного секретного ключа OTP заданной длины.
|
||||||
|
*
|
||||||
|
* @param string $uniqueString Уникальная строка для создания сида.
|
||||||
|
* @param int $length Длина секретного ключа (по умолчанию: 20).
|
||||||
|
* @return string Сгенерированный секретный ключ OTP.
|
||||||
|
*/
|
||||||
|
public static function generateSecret($uniqueString, $length = 20): string
|
||||||
{
|
{
|
||||||
// Уникальная строка + текущее время в микросекундах
|
// Уникальная строка + текущее время в микросекундах
|
||||||
$seed = $uniqueString . microtime(true);
|
$seed = $uniqueString . microtime(true);
|
||||||
@ -28,23 +40,36 @@ readonly class TOTP
|
|||||||
return $secret;
|
return $secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Генерация одноразового пароля (OTP) на основе времени с использованием секретного ключа.
|
||||||
|
*
|
||||||
|
* @param int $digits Количество цифр в OTP (по умолчанию: 6).
|
||||||
|
* @param int $timeStep Временной шаг в секундах (по умолчанию: 30).
|
||||||
|
* @return string Сгенерированный OTP.
|
||||||
|
*/
|
||||||
public function generate(int $digits = 6, int $timeStep = 30): string
|
public function generate(int $digits = 6, int $timeStep = 30): string
|
||||||
{
|
{
|
||||||
$time = floor(time() / $timeStep);
|
$time = floor(time() / $timeStep); // Текущее время, разделенное на временной шаг
|
||||||
$secretKey = $this->base32_decode($this->secret);
|
$secretKey = $this->base32decode($this->secret); // Декодируем секретный ключ из Base32
|
||||||
$binaryTime = pack('N*', 0) . pack('N*', $time);
|
$binaryTime = pack('N*', 0) . pack('N*', $time); // Упаковываем время в бинарный формат (big endian)
|
||||||
$hash = hash_hmac('sha1', $binaryTime, $secretKey, true);
|
$hash = hash_hmac('sha1', $binaryTime, $secretKey, true); // Вычисляем HMAC-SHA1 хеш
|
||||||
$offset = ord($hash[strlen($hash) - 1]) & 0x0F;
|
$offset = ord($hash[strlen($hash) - 1]) & 0x0F; // Получаем смещение из последнего полубайта хеша
|
||||||
$otp = (
|
$otp = (
|
||||||
((ord($hash[$offset]) & 0x7F) << 24) |
|
((ord($hash[$offset]) & 0x7F) << 24) |
|
||||||
((ord($hash[$offset + 1]) & 0xFF) << 16) |
|
((ord($hash[$offset + 1]) & 0xFF) << 16) |
|
||||||
((ord($hash[$offset + 2]) & 0xFF) << 8) |
|
((ord($hash[$offset + 2]) & 0xFF) << 8) |
|
||||||
(ord($hash[$offset + 3]) & 0xFF)
|
(ord($hash[$offset + 3]) & 0xFF)
|
||||||
) % 10 ** $digits;
|
) % 10 ** $digits; // Вычисляем значение OTP
|
||||||
return str_pad((string)$otp, $digits, '0', STR_PAD_LEFT);
|
return str_pad((string)$otp, $digits, '0', STR_PAD_LEFT); // Форматируем OTP до указанной длины
|
||||||
}
|
}
|
||||||
|
|
||||||
private function base32_decode(string $input): string
|
/**
|
||||||
|
* Декодирование строки, закодированной в Base32.
|
||||||
|
*
|
||||||
|
* @param string $input Входная строка, закодированная в Base32.
|
||||||
|
* @return string Декодированная бинарная строка.
|
||||||
|
*/
|
||||||
|
private function base32decode(string $input): string
|
||||||
{
|
{
|
||||||
$base32charsFlipped = array_flip(str_split(self::BASE32CHARS));
|
$base32charsFlipped = array_flip(str_split(self::BASE32CHARS));
|
||||||
$output = '';
|
$output = '';
|
||||||
@ -53,13 +78,13 @@ readonly class TOTP
|
|||||||
|
|
||||||
for ($i = 0, $j = strlen($input); $i < $j; $i++) {
|
for ($i = 0, $j = strlen($input); $i < $j; $i++) {
|
||||||
$v <<= 5;
|
$v <<= 5;
|
||||||
if ($input[$i] == '=') continue;
|
if ($input[$i] == '=') continue; // Пропускаем символы заполнения
|
||||||
$v += $base32charsFlipped[$input[$i]];
|
$v += $base32charsFlipped[$input[$i]]; // Декодируем символ Base32
|
||||||
$vbits += 5;
|
$vbits += 5;
|
||||||
|
|
||||||
if ($vbits >= 8) {
|
if ($vbits >= 8) {
|
||||||
$vbits -= 8;
|
$vbits -= 8;
|
||||||
$output .= chr(($v & (0xFF << $vbits)) >> $vbits);
|
$output .= chr(($v & (0xFF << $vbits)) >> $vbits); // Добавляем декодированный байт
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $output;
|
return $output;
|
||||||
|
Loading…
Reference in New Issue
Block a user