217 Zeilen
9.0 KiB
Python
217 Zeilen
9.0 KiB
Python
"""
|
|
Account Fingerprint Service - Manages account-bound fingerprints.
|
|
"""
|
|
|
|
import hashlib
|
|
import random
|
|
from typing import Optional, Dict, Any
|
|
from datetime import datetime, timedelta
|
|
|
|
from domain.entities.browser_fingerprint import BrowserFingerprint
|
|
from .fingerprint_generator_service import FingerprintGeneratorService
|
|
from .fingerprint_rotation_service import FingerprintRotationService, RotationStrategy
|
|
|
|
|
|
class AccountFingerprintService:
|
|
"""Service for managing account-bound fingerprints."""
|
|
|
|
def __init__(self,
|
|
generator_service: Optional[FingerprintGeneratorService] = None,
|
|
rotation_service: Optional[FingerprintRotationService] = None):
|
|
self.generator_service = generator_service or FingerprintGeneratorService()
|
|
self.rotation_service = rotation_service or FingerprintRotationService()
|
|
|
|
def generate_account_fingerprint(self,
|
|
account_id: str,
|
|
platform: str,
|
|
proxy_location: Optional[str] = None) -> BrowserFingerprint:
|
|
"""Generate a fingerprint bound to a specific account."""
|
|
# Generate base fingerprint with account binding
|
|
fingerprint = self.generator_service.generate_fingerprint(
|
|
platform=platform,
|
|
proxy_location=proxy_location,
|
|
account_id=account_id
|
|
)
|
|
|
|
# Apply deterministic variations based on account ID
|
|
self._apply_account_variations(fingerprint, account_id)
|
|
|
|
return fingerprint
|
|
|
|
def get_daily_fingerprint(self,
|
|
base_fingerprint: BrowserFingerprint,
|
|
account_id: str) -> BrowserFingerprint:
|
|
"""Get deterministic daily variation of account fingerprint."""
|
|
if not base_fingerprint.account_bound:
|
|
raise ValueError("Fingerprint must be account-bound for daily variations")
|
|
|
|
# Calculate days since creation
|
|
days_since_creation = (datetime.now() - base_fingerprint.created_at).days
|
|
|
|
# Generate deterministic seed for today
|
|
today_seed = self._generate_daily_seed(account_id, days_since_creation)
|
|
|
|
# Apply deterministic variations
|
|
varied = self._apply_daily_variations(base_fingerprint, today_seed)
|
|
|
|
return varied
|
|
|
|
def _apply_account_variations(self, fingerprint: BrowserFingerprint, account_id: str) -> None:
|
|
"""Apply account-specific variations to fingerprint."""
|
|
# Use account ID to seed variations
|
|
account_hash = int(hashlib.md5(account_id.encode()).hexdigest()[:8], 16)
|
|
|
|
# Deterministic but unique variations
|
|
random.seed(account_hash)
|
|
|
|
# Vary canvas noise seed within range
|
|
base_seed = fingerprint.canvas_noise.seed
|
|
fingerprint.canvas_noise.seed = base_seed + (account_hash % 1000)
|
|
|
|
# Vary audio latencies slightly
|
|
fingerprint.audio_context_base_latency += (account_hash % 10) * 0.0001
|
|
fingerprint.audio_context_output_latency += (account_hash % 10) * 0.0002
|
|
|
|
# Select subset of fonts deterministically
|
|
if len(fingerprint.font_list) > 5:
|
|
num_to_remove = account_hash % 3 + 1
|
|
for _ in range(num_to_remove):
|
|
fingerprint.font_list.pop(random.randint(0, len(fingerprint.font_list) - 1))
|
|
|
|
# Reset random seed
|
|
random.seed()
|
|
|
|
def _generate_daily_seed(self, account_id: str, day_number: int) -> int:
|
|
"""Generate deterministic seed for a specific day."""
|
|
# Combine account ID with day number
|
|
seed_string = f"{account_id}:{day_number}"
|
|
seed_hash = hashlib.sha256(seed_string.encode()).hexdigest()
|
|
|
|
# Convert to integer seed
|
|
return int(seed_hash[:8], 16)
|
|
|
|
def _apply_daily_variations(self, fingerprint: BrowserFingerprint, daily_seed: int) -> BrowserFingerprint:
|
|
"""Apply deterministic daily variations."""
|
|
# Use rotation service with controlled randomness
|
|
original_seed = random.getstate()
|
|
random.seed(daily_seed)
|
|
|
|
# Minimal rotation for daily changes
|
|
varied = self.rotation_service.rotate_fingerprint(fingerprint, RotationStrategy.MINIMAL)
|
|
|
|
# Additional deterministic changes
|
|
self._apply_time_based_changes(varied, daily_seed)
|
|
|
|
# Restore original random state
|
|
random.setstate(original_seed)
|
|
|
|
return varied
|
|
|
|
def _apply_time_based_changes(self, fingerprint: BrowserFingerprint, seed: int) -> None:
|
|
"""Apply time-based changes that would naturally occur."""
|
|
# Browser version might update weekly
|
|
week_number = seed % 52
|
|
if week_number % 4 == 0: # Every 4 weeks
|
|
self._increment_browser_version(fingerprint)
|
|
|
|
# System uptime affects audio latency
|
|
hour_of_day = datetime.now().hour
|
|
fingerprint.audio_context_base_latency += (hour_of_day / 24) * 0.001
|
|
|
|
# Network conditions affect WebRTC
|
|
if seed % 3 == 0:
|
|
# Change local IP mask (different network)
|
|
fingerprint.webrtc_config.local_ip_mask = f"192.168.{seed % 255}.x"
|
|
|
|
def _increment_browser_version(self, fingerprint: BrowserFingerprint) -> None:
|
|
"""Increment browser version number."""
|
|
import re
|
|
user_agent = fingerprint.navigator_props.user_agent
|
|
|
|
# Find Chrome version
|
|
match = re.search(r'Chrome/(\d+)\.(\d+)\.(\d+)\.(\d+)', user_agent)
|
|
if match:
|
|
major = int(match.group(1))
|
|
minor = int(match.group(2))
|
|
build = int(match.group(3))
|
|
patch = int(match.group(4))
|
|
|
|
# Increment build number
|
|
build += 1
|
|
|
|
# Update user agent
|
|
old_version = match.group(0)
|
|
new_version = f"Chrome/{major}.{minor}.{build}.{patch}"
|
|
fingerprint.navigator_props.user_agent = user_agent.replace(old_version, new_version)
|
|
|
|
def validate_account_binding(self, fingerprint: BrowserFingerprint, account_id: str) -> bool:
|
|
"""Validate that a fingerprint is properly bound to an account."""
|
|
if not fingerprint.account_bound:
|
|
return False
|
|
|
|
if not fingerprint.static_components:
|
|
return False
|
|
|
|
if not fingerprint.rotation_seed:
|
|
return False
|
|
|
|
# Could add more validation here (e.g., check against database)
|
|
return True
|
|
|
|
def get_fingerprint_age_days(self, fingerprint: BrowserFingerprint) -> int:
|
|
"""Get age of fingerprint in days."""
|
|
if not fingerprint.created_at:
|
|
return 0
|
|
|
|
return (datetime.now() - fingerprint.created_at).days
|
|
|
|
def should_rotate_fingerprint(self, fingerprint: BrowserFingerprint) -> bool:
|
|
"""Determine if fingerprint should be rotated."""
|
|
age_days = self.get_fingerprint_age_days(fingerprint)
|
|
|
|
# Rotate after 30 days
|
|
if age_days > 30:
|
|
return True
|
|
|
|
# Check last rotation
|
|
if fingerprint.last_rotated:
|
|
days_since_rotation = (datetime.now() - fingerprint.last_rotated).days
|
|
if days_since_rotation > 7: # Weekly rotation check
|
|
return True
|
|
|
|
return False
|
|
|
|
def prepare_session_fingerprint(self,
|
|
fingerprint: BrowserFingerprint,
|
|
session_data: Dict[str, Any]) -> BrowserFingerprint:
|
|
"""Prepare fingerprint for use with existing session."""
|
|
# Sessions might have slightly different characteristics
|
|
session_fp = self._deep_copy_fingerprint(fingerprint)
|
|
|
|
# Apply session-specific adjustments
|
|
if "browser_version" in session_data:
|
|
self._update_to_browser_version(session_fp, session_data["browser_version"])
|
|
|
|
if "screen_resolution" in session_data:
|
|
session_fp.hardware_config.screen_resolution = tuple(session_data["screen_resolution"])
|
|
|
|
return session_fp
|
|
|
|
def _update_to_browser_version(self, fingerprint: BrowserFingerprint, version: str) -> None:
|
|
"""Update fingerprint to specific browser version."""
|
|
import re
|
|
user_agent = fingerprint.navigator_props.user_agent
|
|
|
|
# Replace Chrome version
|
|
user_agent = re.sub(r'Chrome/[\d.]+', f'Chrome/{version}', user_agent)
|
|
fingerprint.navigator_props.user_agent = user_agent
|
|
|
|
# Update app version
|
|
app_version = fingerprint.navigator_props.app_version
|
|
app_version = re.sub(r'Chrome/[\d.]+', f'Chrome/{version}', app_version)
|
|
fingerprint.navigator_props.app_version = app_version
|
|
|
|
def _deep_copy_fingerprint(self, fingerprint: BrowserFingerprint) -> BrowserFingerprint:
|
|
"""Create a deep copy of fingerprint."""
|
|
# Delegate to rotation service's implementation
|
|
return self.rotation_service._deep_copy_fingerprint(fingerprint) |