""" Device fingerprinting utilities Generates unique fingerprints for devices and browsers """ import hashlib import json from typing import Dict, Optional, Any class DeviceFingerprint: """ Device fingerprinting for security Creates unique fingerprints from device/browser characteristics to detect anomalies and prevent replay attacks """ @staticmethod def generate_browser_fingerprint( user_agent: Optional[str] = None, accept_language: Optional[str] = None, screen_resolution: Optional[str] = None, timezone: Optional[str] = None, fonts: Optional[list] = None, canvas_hash: Optional[str] = None, webgl_hash: Optional[str] = None, plugins: Optional[list] = None ) -> str: """ Generate browser fingerprint from available characteristics Args: user_agent: Browser user agent string accept_language: Accept-Language header screen_resolution: Screen resolution (e.g., "1920x1080") timezone: Browser timezone fonts: List of detected fonts canvas_hash: Canvas fingerprinting hash webgl_hash: WebGL fingerprinting hash plugins: List of browser plugins Returns: str: SHA-256 hash of fingerprint components """ components = { "user_agent": user_agent or "", "accept_language": accept_language or "", "screen_resolution": screen_resolution or "", "timezone": timezone or "", "fonts": sorted(fonts) if fonts else [], "canvas_hash": canvas_hash or "", "webgl_hash": webgl_hash or "", "plugins": sorted(plugins) if plugins else [] } # Create deterministic JSON string fingerprint_json = json.dumps(components, sort_keys=True, separators=(',', ':')) # Hash the fingerprint return hashlib.sha256(fingerprint_json.encode()).hexdigest() @staticmethod def generate_device_fingerprint( platform: str, os_version: Optional[str] = None, device_model: Optional[str] = None, hardware_id: Optional[str] = None, app_version: Optional[str] = None, install_id: Optional[str] = None ) -> str: """ Generate mobile device fingerprint Args: platform: Device platform (ios/android) os_version: OS version device_model: Device model hardware_id: Hardware identifier app_version: App version install_id: Unique installation ID Returns: str: SHA-256 hash of device components """ components = { "platform": platform.lower(), "os_version": os_version or "", "device_model": device_model or "", "hardware_id": hardware_id or "", "app_version": app_version or "", "install_id": install_id or "" } fingerprint_json = json.dumps(components, sort_keys=True, separators=(',', ':')) return hashlib.sha256(fingerprint_json.encode()).hexdigest() @staticmethod def generate_simple_fingerprint( user_agent: Optional[str] = None, ip_address: Optional[str] = None, accept_headers: Optional[Dict[str, str]] = None ) -> str: """ Generate simple fingerprint from request headers Used as fallback when detailed fingerprinting is not available """ components = { "user_agent": user_agent or "", "ip_address": ip_address or "", "accept": accept_headers.get("accept", "") if accept_headers else "", "accept_language": accept_headers.get("accept-language", "") if accept_headers else "", "accept_encoding": accept_headers.get("accept-encoding", "") if accept_headers else "" } fingerprint_json = json.dumps(components, sort_keys=True, separators=(',', ':')) return hashlib.sha256(fingerprint_json.encode()).hexdigest() @staticmethod def compare_fingerprints( fingerprint1: str, fingerprint2: str, tolerance: float = 0.0 ) -> bool: """ Compare two fingerprints Args: fingerprint1: First fingerprint fingerprint2: Second fingerprint tolerance: Similarity threshold (0.0 = exact match) Returns: bool: True if fingerprints match within tolerance """ if tolerance == 0.0: return fingerprint1 == fingerprint2 # For fuzzy matching, we could implement similarity algorithms # For now, exact match only return fingerprint1 == fingerprint2 @staticmethod def extract_fingerprint_components(fingerprint_hash: str) -> Dict[str, Any]: """ Extract components from a fingerprint (if stored separately) Note: This is a placeholder - in production, components would be stored alongside the hash for analysis """ return { "hash": fingerprint_hash, "algorithm": "sha256", "components_stored": False }