base32decode($this->secret); // Декодируем секретный ключ из Base32 $binaryTime = pack('N*', 0) . pack('N*', $time); // Упаковываем время в бинарный формат (big endian) $hash = hash_hmac('sha1', $binaryTime, $secretKey, true); // Вычисляем HMAC-SHA1 хеш $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; // Вычисляем значение OTP return str_pad((string)$otp, $digits, '0', STR_PAD_LEFT); // Форматируем OTP до указанной длины } /** * Декодирование строки, закодированной в Base32. * * @param string $input Входная строка, закодированная в Base32. * @return string Декодированная бинарная строка. */ private function base32decode(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]]; // Декодируем символ Base32 $vbits += 5; if ($vbits >= 8) { $vbits -= 8; $output .= chr(($v & (0xFF << $vbits)) >> $vbits); // Добавляем декодированный байт } } return $output; } }