feat(licence): refuser les licences non liees a un poste

Une cle flottante etait sortie. Un schema hors ligne n'a pas de
revocation — rien ne peut aller dire a un exe installe qu'une cle ne vaut
plus — donc la seule prise est de cesser d'accepter toute la classe.

MachineMatches exige desormais une empreinte, et -Floating disparait du
script : il ne peut plus fabriquer une cle que l'exe rejetterait.

Effet de bord assume : plus de licence de site, et un poste sans
empreinte exploitable n'est plus licenciable du tout — mieux qu'une
empreinte degeneree qui ouvrirait partout.

Verifie : liee a ce poste acceptee, liee a un autre refusee, les deux
flottantes deja emises refusees.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-09-05 15:38:07 +01:00
parent 405ac7fac4
commit 988694b20d
3 changed files with 40 additions and 31 deletions
+11 -5
View File
@@ -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
+16 -8
View File
@@ -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.');
+12 -17
View File
@@ -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 <empreinte>.
@@ -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) {
# 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
''