calculateCode($secret, (int)($currentTime + $i)) === $code) { return true; } } return false; } private function calculateCode(string $secret, int $time): string { $key = $this->base32Decode($secret); $timeHex = str_pad(dechex($time), 16, '0', STR_PAD_LEFT); $timeBin = pack('H*', $timeHex); $hash = hash_hmac('sha1', $timeBin, $key, true); $offset = ord($hash[19]) & 0xf; $otp = ( ((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)$otp, 6, '0', STR_PAD_LEFT); } private function base32Decode(string $base32): string { $base32 = strtoupper($base32); $buffer = 0; $bufferSize = 0; $decoded = ''; for ($i = 0; $i < strlen($base32); $i++) { $char = $base32[$i]; $pos = strpos(self::ALPHABET, $char); if ($pos === false) continue; $buffer = ($buffer << 5) | $pos; $bufferSize += 5; if ($bufferSize >= 8) { $bufferSize -= 8; $decoded .= chr(($buffer >> $bufferSize) & 0xff); } } return $decoded; } public function getQrCodeUrl(string $userEmail, string $secret, string $issuer = 'Musadaq'): string { $label = urlencode($issuer . ':' . $userEmail); $otpauth = "otpauth://totp/{$label}?secret={$secret}&issuer=" . urlencode($issuer); return "https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=" . urlencode($otpauth); } }