feat(licence): lier la licence au poste (carte mere + disque)

C'etait la decision prise, et je l'avais mal lue : "pour le moment 1"
repondait a la question 1, pas "une seule licence".

PM.MachineId melange l'UUID de la carte mere (WMI) et la serie du volume
systeme, puis n'en publie que 16 hexa : l'empreinte se recopie dans un
courriel, l'UUID brut n'a pas a circuler. Un UUID nul ou tout a FF est
rejete — VM et images clonees l'annoncent, et deux postes tomberaient
alors sur la meme empreinte.

Le champ m est compare APRES la signature. m absent = licence flottante,
acceptee : seule la cle privee peut en emettre, et le champ ne peut pas
etre retire sans casser la signature. Cote script -Machine est
obligatoire sauf -Floating explicite.

L'ecran d'activation affiche l'Installation ID : une licence liee ne peut
plus etre emise avant l'installation.

Verifie sur ce poste : liee a lui acceptee, liee a un autre refusee,
flottante acceptee.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-09-05 15:16:35 +01:00
parent 2cb7d29450
commit 405ac7fac4
10 changed files with 287 additions and 8 deletions
+42 -2
View File
@@ -34,6 +34,7 @@ type
Holder: string; // nom affichable
Email: string;
IssuedOn: string; // 'yyyy-mm-dd', date d'emission (PAS une expiration)
Machine: string; // empreinte du poste, vide = licence flottante
end;
// Verifie une chaine de licence sans rien ecrire. False = signature invalide,
@@ -51,6 +52,10 @@ function IsLicensed: Boolean;
function LicenceInfo: TLicenceInfo;
procedure ClearLicence;
// L'empreinte de CE poste, a montrer sur l'ecran d'activation : sans elle
// l'utilisateur n'a rien a envoyer pour qu'on lui emette une licence liee.
function ThisMachineId: string;
// Verifie le chemin complet contre un vecteur de test fige. Appele a
// l'initialisation de l'unite, comme SelfTestCrypto.
procedure SelfTestLicence;
@@ -58,7 +63,8 @@ procedure SelfTestLicence;
implementation
uses
System.JSON, System.Hash, System.NetEncoding, Winapi.Windows, PM.UserPrefs;
System.JSON, System.Hash, System.NetEncoding, Winapi.Windows,
PM.UserPrefs, PM.MachineId;
const
LicencePrefKey = 'licenceKey';
@@ -182,6 +188,7 @@ begin
AInfo.Holder := LObj.GetValue<string>('n', '');
AInfo.Email := LObj.GetValue<string>('e', '');
AInfo.IssuedOn := LObj.GetValue<string>('d', '');
AInfo.Machine := LowerCase(LObj.GetValue<string>('m', ''));
Result := AInfo.Email <> '';
finally
LVal.Free;
@@ -211,6 +218,20 @@ begin
Result := ParsePayload(LMsg, AInfo);
end;
// Le liage au poste, verifie APRES la signature — l'empreinte n'a de sens que
// dans une charge utile dont on sait deja qu'elle vient de nous.
//
// UNE EMPREINTE ABSENTE PASSE, ET C'EST VOULU : c'est une licence flottante,
// que seule la cle privee peut emettre. Ce n'est pas un contournement — un
// tiers ne peut pas retirer le champ d'une licence liee sans casser la
// signature. Le script d'emission, lui, exige -Floating pour en fabriquer une,
// pour qu'elle ne sorte jamais par distraction.
function MachineMatches(const AInfo: TLicenceInfo): Boolean;
begin
Result := (AInfo.Machine = '') or
SameText(AInfo.Machine, PM.MachineId.MachineFingerprint);
end;
// ===== API ===================================================================
var
@@ -226,7 +247,8 @@ end;
function VerifyLicence(const ALicence: string; out AInfo: TLicenceInfo): Boolean;
begin
Result := SplitAndVerify(ProductionKey, Trim(ALicence), AInfo);
Result := SplitAndVerify(ProductionKey, Trim(ALicence), AInfo) and
MachineMatches(AInfo);
end;
function ActivateLicence(const ALicence: string; out AInfo: TLicenceInfo): Boolean;
@@ -266,6 +288,11 @@ begin
Result := CachedInfo;
end;
function ThisMachineId: string;
begin
Result := PM.MachineId.MachineFingerprintDisplay;
end;
procedure ClearLicence;
begin
SetPref(LicencePrefKey, '');
@@ -310,6 +337,19 @@ begin
'u9q3GvBByVU0pLbuRrgT98-kZqOBSnYptJ2Mx1B1OjXxJAFgNgnxyjj8SmYpkFt1GuP2PlzRXB3A3HYRMwW2gA';
if SplitAndVerify(LKey, LTampered, LInfo) then
raise Exception.Create('Auto-test licence ECHOUE : une licence trafiquee est acceptee.');
// Le liage au poste, eprouve sur l'enregistrement : on ne peut pas signer un
// vecteur lie sans la cle privee de test, jetee — mais c'est MachineMatches
// qui porte la regle, et c'est lui qu'il faut voir tomber.
LInfo.Machine := '';
if not MachineMatches(LInfo) then
raise Exception.Create('Auto-test licence ECHOUE : une licence flottante est refusee.');
LInfo.Machine := 'ffffffffffffffff';
if MachineMatches(LInfo) then
raise Exception.Create('Auto-test licence ECHOUE : une licence d''un AUTRE poste est acceptee.');
LInfo.Machine := PM.MachineId.MachineFingerprint;
if not MachineMatches(LInfo) then
raise Exception.Create('Auto-test licence ECHOUE : la licence de CE poste est refusee.');
end;
initialization