import 'dart:convert'; import 'dart:io' show Platform; import 'dart:math'; import 'package:crypto/crypto.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; /// Enterprise Real Device Fingerprint & Hardware Identity Service (Directorate Admin) class DeviceFingerprintService { static final DeviceInfoPlugin _deviceInfoPlugin = DeviceInfoPlugin(); static String? _cachedFingerprint; static Map? _cachedMetadata; static const String _storageKeyDeviceId = 'saqel_admin_device_persistent_id'; /// Returns the deterministic, physical/hardware-based device fingerprint. /// Format: saqel_{platform}_{sha256_hash_first_32_chars} static Future getFingerprint() async { if (_cachedFingerprint != null && _cachedFingerprint!.isNotEmpty) { return _cachedFingerprint!; } final metadata = await getDeviceMetadata(); _cachedFingerprint = metadata['device_fingerprint']!; return _cachedFingerprint!; } /// Extracts comprehensive physical/hardware metadata from the running platform. static Future> getDeviceMetadata() async { if (_cachedMetadata != null) { return _cachedMetadata!; } String platform = 'unknown'; String hardwareId = ''; String model = ''; String brand = ''; String osVersion = ''; String deviceName = ''; try { if (kIsWeb) { platform = 'web'; final web = await _deviceInfoPlugin.webBrowserInfo; hardwareId = '${web.vendor}_${web.platform}_${web.hardwareConcurrency}'; model = web.browserName.name; brand = web.vendor ?? 'Web'; osVersion = web.userAgent ?? 'UnknownWeb'; deviceName = 'Web Browser'; } else if (Platform.isAndroid) { platform = 'android'; final android = await _deviceInfoPlugin.androidInfo; hardwareId = android.id.isNotEmpty ? android.id : android.fingerprint; model = android.model; brand = android.brand; osVersion = 'Android ${android.version.release} (SDK ${android.version.sdkInt})'; deviceName = '${android.brand} ${android.model}'; } else if (Platform.isIOS) { platform = 'ios'; final ios = await _deviceInfoPlugin.iosInfo; hardwareId = ios.identifierForVendor ?? ''; model = ios.model; brand = 'Apple'; osVersion = 'iOS ${ios.systemVersion}'; deviceName = ios.name; } else if (Platform.isMacOS) { platform = 'macos'; final mac = await _deviceInfoPlugin.macOsInfo; hardwareId = mac.systemGUID ?? ''; model = mac.model; brand = 'Apple'; osVersion = 'macOS ${mac.osRelease}'; deviceName = mac.computerName; } else if (Platform.isWindows) { platform = 'windows'; final win = await _deviceInfoPlugin.windowsInfo; hardwareId = win.deviceId; model = win.productName; brand = 'Microsoft'; osVersion = 'Windows ${win.majorVersion}.${win.minorVersion}.${win.buildNumber}'; deviceName = win.computerName; } else if (Platform.isLinux) { platform = 'linux'; final linux = await _deviceInfoPlugin.linuxInfo; hardwareId = linux.machineId ?? ''; model = linux.name; brand = linux.id; osVersion = linux.versionId ?? ''; deviceName = linux.prettyName; } } catch (_) { platform = kIsWeb ? 'web' : (Platform.isAndroid ? 'android' : (Platform.isIOS ? 'ios' : 'desktop')); } final persistentId = await _getOrCreatePersistentInstallationId(); final rawSeed = 'saqel_hw:$platform:$hardwareId:$brand:$model:$osVersion:$persistentId'; final hash = sha256.convert(utf8.encode(rawSeed)).toString(); final fingerprint = 'saqel_${platform}_${hash.substring(0, 32)}'; _cachedMetadata = { 'device_fingerprint': fingerprint, 'platform': platform, 'hardware_id': hardwareId, 'model': model, 'brand': brand, 'os_version': osVersion, 'device_name': deviceName, 'persistent_install_id': persistentId, }; return _cachedMetadata!; } static Future _getOrCreatePersistentInstallationId() async { try { final prefs = await SharedPreferences.getInstance(); var id = prefs.getString(_storageKeyDeviceId); if (id != null && id.isNotEmpty) { return id; } final rand = Random.secure(); final bytes = List.generate(16, (_) => rand.nextInt(256)); bytes[6] = (bytes[6] & 0x0f) | 0x40; bytes[8] = (bytes[8] & 0x3f) | 0x80; final hex = bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join(); id = '${hex.substring(0, 8)}-${hex.substring(8, 12)}-${hex.substring(12, 16)}-${hex.substring(16, 20)}-${hex.substring(20)}'; await prefs.setString(_storageKeyDeviceId, id); return id; } catch (_) { return 'fallback-${DateTime.now().millisecondsSinceEpoch}'; } } static void resetCache() { _cachedFingerprint = null; _cachedMetadata = null; } }