diff --git a/CLAUDE.md b/CLAUDE.md index a0199b7..47bd892 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -300,11 +300,17 @@ que dans une charge utile dont on sait deja qu'elle vient de nous. installation. Les deux sources absentes → `'unavailable'`, qui ne correspond a aucune licence : mieux qu'une chaine vide, identique partout. - Calculee une fois puis retenue (l'appel WMI coute quelques centaines de ms). -- **`m` absent = licence flottante, acceptee** — seule la cle privee peut en - emettre une, et un tiers ne peut pas retirer le champ sans casser la - signature. Ce n'est donc pas un contournement, c'est une licence de site. - Cote script, `-Machine` est **obligatoire** sauf `-Floating` explicite : - l'oubli produirait sinon la licence la plus permissive en silence. +- **`m` absent = licence REFUSEE.** Les licences flottantes ont existe le temps + d'une seance et l'une d'elles est sortie ; les refuser en bloc est la **seule + facon de l'annuler**. Un schema hors ligne n'a pas de revocation — rien ne + peut aller dire a un exe deja installe qu'une cle ne vaut plus — donc la seule + prise qui reste est de cesser d'accepter toute une classe de cles. Le cout + assume : plus de licence de site, une cle par poste. `-Floating` a ete retire + du script, qui ne peut donc plus fabriquer une cle que l'exe rejetterait. +- Un poste dont l'empreinte vaut `'unavailable'` (ni WMI ni MachineGuid) ne peut + plus etre licencie : le script n'accepte que 16 hexa. C'est le bon sens de + l'erreur — mieux vaut un poste qu'on ne peut pas licencier qu'une empreinte + degeneree qui ouvrirait partout. - **Consequence de flux** : on ne peut plus emettre une licence avant que le client ait installe. L'ecran d'activation affiche l'Installation ID, il l'envoie, on emet. Changer la carte mere ou le disque systeme casse la diff --git a/delphi-backend/Source/PM.Licence.pas b/delphi-backend/Source/PM.Licence.pas index 2980e36..18bdaa2 100644 --- a/delphi-backend/Source/PM.Licence.pas +++ b/delphi-backend/Source/PM.Licence.pas @@ -221,14 +221,22 @@ 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. +// UNE EMPREINTE ABSENTE EST REFUSEE. Les licences flottantes ont existe le +// temps d'une seance, et l'une d'elles est sortie ; les refuser est la SEULE +// facon de l'annuler. Un schema hors ligne n'a pas de revocation — rien ne +// peut aller dire a un exe deja installe qu'une cle ne vaut plus — donc la +// seule prise qui reste est de cesser d'accepter toute une classe de cles. +// +// Ce que ca coute : plus de licence de site, une cle par poste. Assume. +// +// Consequence de bord : un poste dont l'empreinte vaut 'unavailable' (ni WMI +// ni MachineGuid) ne peut plus etre licencie du tout, puisque le script +// d'emission n'accepte que 16 caracteres hexadecimaux. C'est le bon sens de +// l'erreur : mieux vaut un poste qu'on ne peut pas licencier qu'une empreinte +// degeneree qui ouvrirait partout. function MachineMatches(const AInfo: TLicenceInfo): Boolean; begin - Result := (AInfo.Machine = '') or + Result := (AInfo.Machine <> '') and SameText(AInfo.Machine, PM.MachineId.MachineFingerprint); end; @@ -342,8 +350,8 @@ begin // 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.'); + if MachineMatches(LInfo) then + raise Exception.Create('Auto-test licence ECHOUE : une licence flottante est acceptee.'); LInfo.Machine := 'ffffffffffffffff'; if MachineMatches(LInfo) then raise Exception.Create('Auto-test licence ECHOUE : une licence d''un AUTRE poste est acceptee.'); diff --git a/tools/licence.ps1 b/tools/licence.ps1 index 89379ee..6784c4d 100644 --- a/tools/licence.ps1 +++ b/tools/licence.ps1 @@ -15,7 +15,6 @@ param( [string]$For, [string]$Name = '', [string]$Machine, - [switch]$Floating, [string]$KeyFile = "$PSScriptRoot\licence-private.key" ) @@ -79,17 +78,17 @@ $p.Q = $q $ecdsa = [System.Security.Cryptography.ECDsa]::Create($p) -# LE LIAGE AU POSTE EST LE DEFAUT, ET IL FAUT LE DIRE POUR Y COUPER. +# LE LIAGE AU POSTE EST OBLIGATOIRE, ET IL N'Y A PLUS DE DEROGATION. # -# Une licence flottante s'active sur autant de machines qu'on veut : c'est -# parfois voulu (licence de site), jamais par distraction. -Floating l'exige -# explicitement, sinon l'oubli de -Machine produirait en silence la licence -# la plus permissive - exactement le defaut qu'on ne veut pas. +# Les licences flottantes (sans champ m) existaient ; l'exe les refuse +# desormais, ce qui etait la seule facon d'annuler celle qui etait sortie - +# un schema hors ligne n'a pas de revocation. En emettre une aujourd'hui +# produirait une cle que l'application rejettera : autant ne pas pouvoir. # # L'empreinte est celle qu'affiche l'ecran d'activation sur le poste du # client : il faut donc qu'il ait installe l'application AVANT qu'on emette # sa licence. C'est le prix du liage. -if (-not $Machine -and -not $Floating) { +if (-not $Machine) { throw @' Il manque -Machine . @@ -98,8 +97,7 @@ Fais-toi envoyer l'empreinte affichee sur l'ecran d'activation du poste .\licence.ps1 -For "a@b.com" -Name "Nom" -Machine "XXXX-XXXX-XXXX-XXXX" -Pour une licence NON liee, qui s'activera sur n'importe quelle machine, -demande-la explicitement avec -Floating. +Une licence non liee n'est plus emettable : l'application la refuserait. '@ } @@ -107,13 +105,11 @@ demande-la explicitement avec -Floating. # Pas d'expiration - une date qu'on fait respecter avec l'horloge de la # machine du client ne prouve rien. A ajouter le jour ou on vend un abonnement. $payload = [ordered]@{ n = $Name; e = $For; d = (Get-Date).ToString('yyyy-MM-dd') } -if ($Machine) { - # Les tirets ne sont qu'une aide a la recopie ; Delphi compare le condense - # nu, en minuscules. - $payload.m = ($Machine -replace '[^0-9A-Fa-f]', '').ToLower() - if ($payload.m.Length -ne 16) { - throw "Empreinte attendue : 16 caracteres hexadecimaux (recu $($payload.m.Length))." - } +# Les tirets ne sont qu'une aide a la recopie ; Delphi compare le condense +# nu, en minuscules. +$payload.m = ($Machine -replace '[^0-9A-Fa-f]', '').ToLower() +if ($payload.m.Length -ne 16) { + throw "Empreinte attendue : 16 caracteres hexadecimaux (recu $($payload.m.Length))." } $json = ($payload | ConvertTo-Json -Compress) $bytes = [Text.Encoding]::UTF8.GetBytes($json) @@ -122,8 +118,7 @@ $sig = $ecdsa.SignData($bytes, [System.Security.Cryptography.HashAlgorithmNa $licence = (ToB64Url $bytes) + '.' + (ToB64Url $sig) '' -if ($Machine) { 'Licence pour ' + $For + ' — liee au poste ' + $Machine } -else { 'Licence pour ' + $For + ' — FLOTTANTE (toute machine)' } +'Licence pour ' + $For + ' — liee au poste ' + $Machine '' $licence ''