Pourquoi un fingerprint hardware ?
Un système de licensing basé uniquement sur une clé de licence est insuffisant : la clé peut être copiée et partagée. La solution est de lier cryptographiquement la licence à une machine précise via son empreinte matérielle.
L'empreinte hardware est un hash calculé à partir de plusieurs caractéristiques physiques de la machine. Sur la même machine, le hash doit être reproductible. Sur une machine différente, il doit être différent. Et si l'utilisateur change un composant (une RAM, une carte réseau), le système doit être tolérant à un certain degré de changement sans casser la licence.
Un bon fingerprint hardware résiste à trois attaques : la copie de la licence sur une autre machine, la virtualisation (VM), et le clonage disque.
Les 8 sources d'entropie
IronLock v2.0 combine 8 sources indépendantes. Chaque source est collectée, normalisée, puis intégrée dans un hash SHA-256 final.
Collecte cross-platform
La collecte doit fonctionner sur Windows et Linux sans droits administrateur. IronLock utilise des APIs système disponibles en userland :
import platform, hashlib, subprocess, uuid
from typing import Optional
def _get_windows_wmic(wmic_path: str) -> Optional[str]:
try:
r = subprocess.run(
['wmic'] + wmic_path.split() + ['get', 'Value', '--format:list'],
capture_output=True, text=True, timeout=5
)
lines = [l.split('=')[1].strip() for l in r.stdout.splitlines()
if '=' in l and l.split('=')[1].strip()]
return lines[0] if lines else None
except: return None
def collect_sources() -> dict:
win = platform.system() == 'Windows'
sources = {}
# Source 1 : UUID carte mère
if win:
sources['mb_uuid'] = _get_windows_wmic('csproduct UUID')
else:
try:
sources['mb_uuid'] = open('/sys/class/dmi/id/product_uuid').read().strip()
except: sources['mb_uuid'] = None
# Source 2 : BIOS serial
if win:
sources['bios_serial'] = _get_windows_wmic('bios serialnumber')
else:
try:
sources['bios_serial'] = open('/sys/class/dmi/id/product_serial').read().strip()
except: sources['bios_serial'] = None
# Source 4 : MAC address
sources['mac'] = ':'.join(
['{:02x}'.format((uuid.getnode() >> 8*i) & 0xff) for i in reversed(range(6))]
)
# Source 7 : OS install GUID
if win:
import winreg
k = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,
r'SOFTWARE\Microsoft\Cryptography')
sources['os_guid'] = winreg.QueryValueEx(k, 'MachineGuid')[0]
else:
try:
sources['os_guid'] = open('/etc/machine-id').read().strip()
except: sources['os_guid'] = None
return sources
Combinaison et hachage SHA-256
Les sources sont concaténées dans un ordre déterministe, normalisées (minuscules, espaces supprimés), puis hachées. Les sources absentes (None) sont remplacées par un placeholder constant pour ne pas perturber la longueur :
def build_fingerprint(sources: dict, salt: bytes = b'IRONLOCK_V2') -> str:
ORDER = ['mb_uuid', 'bios_serial', 'cpu_id', 'mac',
'disk_serial', 'ram_config', 'os_guid', 'hostname_hash']
parts = []
for key in ORDER:
val = sources.get(key) or f'MISSING_{key.upper()}'
# Normalisation : minuscules, sans espaces ni tirets
val = val.lower().replace('-', '').replace(' ', '')
parts.append(val)
raw = '|'.join(parts).encode('utf-8')
return hashlib.sha256(salt + raw).hexdigest()
# Exemple de résultat
fp = build_fingerprint(collect_sources())
# → 'a3f8c2e1d4b7f09a...64 caractères hexadécimaux'
Tolérance aux changements matériels
Un utilisateur légitime peut changer de RAM, de carte réseau ou réinstaller son OS. Le système de tolérance d'IronLock v2 fonctionne par score de concordance : chaque source vaut un certain poids, et la licence est valide tant que le score dépasse un seuil configurable :
WEIGHTS = {
'mb_uuid': 3, # critique
'bios_serial': 3, # critique
'disk_serial': 2, # important
'cpu_id': 2,
'os_guid': 1,
'mac': 1,
'ram_config': 1,
'hostname_hash':1,
}
THRESHOLD = 10 # sur 14 points max — tolérance ~30%
def verify_tolerance(stored: dict, current: dict) -> bool:
score = sum(WEIGHTS[k] for k, w in WEIGHTS.items()
if stored.get(k) == current.get(k))
return score >= THRESHOLD
Détection de virtualisation
Les machines virtuelles retournent des valeurs prévisibles ou absentes pour plusieurs sources. IronLock détecte les VMs en croisant plusieurs indicateurs :
- UUID carte mère — les VMs VMware retournent des UUID commençant par
564d. VirtualBox :080027. - MAC address — préfixes connus :
00:0C:29VMware,08:00:27VirtualBox,52:54:00QEMU. - BIOS serial — les VMs légères retournent
Noneou une chaîne vide. - RAM config — les VMs cloud ont souvent des configurations RAM non standard (2047 MB, puissances de 2 exactes).
Implémentation dans IronLock v2
IronLock v2 automatise tout ce processus. Le fingerprint est généré en une ligne et exporté en JSON pour la gestion des licences :
# Générer le fingerprint de la machine courante
ironlock fingerprint --output fingerprint.json
# fingerprint.json (envoyé par le client pour activation)
{
"hardware_id": "a3f8c2e1d4b7...",
"sources": {
"mb_uuid": "presente",
"mac": "presente"
// valeurs hachées, jamais en clair
},
"version": "2.0",
"timestamp": "2026-02-14T10:30:00Z"
}
Conclusion
Un fingerprint hardware robuste nécessite au minimum 6 à 8 sources indépendantes, une normalisation stricte et un système de tolérance aux changements légitimes. La combinaison UUID carte mère + BIOS serial + Disk serial constitue le noyau irréductible. Les autres sources servent à la tolérance et à la détection de VM.
IronLock v2 implémente ce système complet, y compris la gestion des absences de sources, la détection de virtualisation et le scoring de concordance — sans que vous ayez à gérer ces cas un par un.