Files
2026-10-16 19:12:18 +00:00

76 lines
2.3 KiB
PHP

<?php
/**
* Minimale TOTP-Implementierung (RFC 6238) ohne externe Abhängigkeiten.
* Kompatibel mit Google Authenticator, Authy, etc.
*/
function totp_generate_secret(int $length = 16): string
{
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
$secret = '';
$bytes = random_bytes($length);
for ($i = 0; $i < $length; $i++) {
$secret .= $chars[ord($bytes[$i]) & 31];
}
return $secret;
}
function totp_get_code(string $secret, int $timeSlot = 0): string
{
$time = floor(time() / 30) + $timeSlot;
$key = base32_decode($secret);
$msg = pack('N*', 0) . pack('N*', $time);
$hash = hash_hmac('sha1', $msg, $key, true);
$offset = ord($hash[19]) & 0xF;
$code = (
((ord($hash[$offset]) & 0x7F) << 24) |
((ord($hash[$offset + 1]) & 0xFF) << 16) |
((ord($hash[$offset + 2]) & 0xFF) << 8) |
((ord($hash[$offset + 3]) & 0xFF))
) % 1000000;
return str_pad((string) $code, 6, '0', STR_PAD_LEFT);
}
function totp_verify(string $secret, string $code, int $window = 1): bool
{
$code = preg_replace('/\s+/', '', $code);
if (!preg_match('/^\d{6}$/', $code)) return false;
for ($i = -$window; $i <= $window; $i++) {
if (hash_equals(totp_get_code($secret, $i), $code)) return true;
}
return false;
}
function totp_qr_url(string $secret, string $email, string $issuer = 'DeskHub'): string
{
$label = rawurlencode($issuer . ':' . $email);
$params = http_build_query([
'secret' => $secret,
'issuer' => $issuer,
'algorithm' => 'SHA1',
'digits' => 6,
'period' => 30,
]);
$otpauth = 'otpauth://totp/' . $label . '?' . $params;
return 'https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=' . rawurlencode($otpauth);
}
function base32_decode(string $input): string
{
$map = array_flip(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'));
$input = strtoupper(rtrim($input, '='));
$binary = '';
$buffer = 0;
$bits = 0;
foreach (str_split($input) as $char) {
if (!isset($map[$char])) continue;
$buffer = ($buffer << 5) | $map[$char];
$bits += 5;
if ($bits >= 8) {
$binary .= chr(($buffer >> ($bits - 8)) & 0xFF);
$bits -= 8;
}
}
return $binary;
}