"""Maschinen-Fingerprint der lizenzierten Instanz. Jede Bindungs-/Autorisierungsentscheidung des Protokolls laeuft ueber diesen Wert -- nie ueber ``hostname`` oder ``license_id`` allein. Aenderung gegenueber dem Vorgaenger (bewusst): der Hostname ist **nicht** mehr Teil des Hashes. Im Vorgaengerprojekt war ``sha256(machine_id + ":" + hostname)`` im Einsatz, wodurch eine reine Umbenennung des Hosts den Fingerprint aenderte und die Lizenz "already_bound_elsewhere" lief -- ein Support-Fall ohne Sicherheitsgewinn. Jetzt zaehlt ausschliesslich die stabile Maschinenidentitaet (``/etc/machine-id``, ergaenzt um die DMI-Produkt-UUID, falls lesbar). """ from __future__ import annotations import hashlib import socket from pathlib import Path _MACHINE_ID_PATHS = (Path("/etc/machine-id"), Path("/var/lib/dbus/machine-id")) _DMI_UUID_PATH = Path("/sys/class/dmi/id/product_uuid") FINGERPRINT_CONTEXT = b"tesm/machine-fingerprint/v2\x00" def _read_first(paths: tuple[Path, ...]) -> str: for path in paths: try: value = path.read_text(encoding="utf-8", errors="strict").strip() except OSError: continue if value: return value return "" def machine_identity() -> str: """Rohe, ungehashte Maschinenidentitaet -- nur fuer Diagnose/Tests.""" machine_id = _read_first(_MACHINE_ID_PATHS) dmi_uuid = "" try: dmi_uuid = _DMI_UUID_PATH.read_text(encoding="utf-8").strip() except OSError: pass if machine_id or dmi_uuid: return f"{machine_id}|{dmi_uuid}" # Letzter Ausweg (Windows/Container ohne machine-id): Hostname. Produktivziel # ist Linux, dort greift dieser Zweig nicht. return f"hostname:{socket.gethostname()}" def system_fingerprint() -> str: """SHA-256-Hex-Digest der Maschinenidentitaet, domaenengetrennt gehasht.""" return hashlib.sha256(FINGERPRINT_CONTEXT + machine_identity().encode("utf-8")).hexdigest()