| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- unit Shared.LicenseFunc;
- {
- Inno Setup
- Copyright (C) 1997-2025 Jordan Russell
- Portions by Martijn Laan
- For conditions of distribution and use, see LICENSE.TXT.
- License functions used by both IDE and ISCC units
- }
- interface
- type
- TLicenseType = (ltSingle, ltTeam, ltEnterprise);
- TLicense = record
- Key, Name: String;
- Typ: TLicenseType;
- ExpirationDate: TDateTime;
- end;
- { "Expired" means update entitlement ended }
- TLicenseState = (lsNotLicensed, lsLicensed, lsExpiring, lsExpired, lsExpiredButUpdated);
- procedure ReadLicense;
- procedure WriteLicense;
- procedure RemoveLicense;
- function ParseLicenseKey(const LicenseKey: String; out License: TLicense): Boolean;
- function UpdateLicense(const LicenseKey: String): Boolean; overload;
- procedure UpdateLicense(const ALicense: TLicense); overload;
- function IsLicensed: Boolean;
- function GetLicenseKey: String;
- function GetChunkedLicenseKey: String;
- function GetLicenseState: TLicenseState;
- function GetLicenseeName: String;
- function GetLicenseeDescription: String;
- function GetLicenseTypeDescription: String;
- function GetLicenseDescription(const Prefix, Separator: String): String;
- implementation
- uses
- Windows,
- SysUtils, Classes, DateUtils, NetEncoding, RegularExpressions,
- ECDSA, SHA256, UnsignedFunc, Shared.ConfigIniFile;
- var
- License: TLicense;
- procedure ReadLicense;
- begin
- const Ini = TConfigIniFile.Create;
- try
- UpdateLicense(Ini.ReadString('License', 'LicenseKey', ''));
- finally
- Ini.Free;
- end;
- end;
- procedure WriteLicense;
- begin
- const Ini = TConfigIniFile.Create;
- try
- Ini.WriteString('License', 'LicenseKey', License.Key);
- finally
- Ini.Free;
- end;
- end;
- procedure RemoveLicense;
- begin
- const Ini = TConfigIniFile.Create;
- try
- Ini.DeleteKey('License', 'LicenseKey');
- finally
- Ini.Free;
- end;
- UpdateLicense('');
- end;
- function ParseLicenseKey(const LicenseKey: String; out License: TLicense): Boolean;
- function ECDSAInt256FromString(const S: String): TECDSAInt256;
- begin
- TSHA256Digest(Result) := SHA256DigestFromString(S);
- end;
- function TryDateFromDBDate(const S: string; out D: TDate): Boolean;
- begin
- const N = S.Length;
- if N = 8 then begin
- const Year = Word.Parse(Copy(S, 1, 4));
- const Month = Word.Parse(Copy(S, 5, 2));
- const Day = Word.Parse(Copy(S, 7, 2));
- D := EncodeDate(Year, Month, Day);
- Result := True;
- end else if N = 0 then begin
- D := 0;
- Result := True;
- end else
- Result := False;
- end;
- begin
- Result := False;
- const CleanLicenseKey = TRegEx.Replace(LicenseKey, '\s+', '');
- const N = Length(CleanLicenseKey);
- if N > 92 then begin { 92 = (64/3*4 rounded to a multiple of 4) + 4 }
- if (Copy(CleanLicenseKey, 1, 2) = 'in') and (Copy(CleanLicenseKey, N-1, 2) = 'no') then begin
- var EncodedKey := Copy(CleanLicenseKey, 3, N-4); { Strip 'in' and 'no' }
- EncodedKey := EncodedKey + StringOfChar('=', (4 - Length(EncodedKey) mod 4) mod 4); { Restore base64 padding }
- const DecodedKey = TNetEncoding.Base64.DecodeStringToBytes(EncodedKey);
- if Length(DecodedKey) > 64 then begin
- var Signature := Default(TECDSASignature);
- Move(DecodedKey[0], Signature.Sig_r[0], 32);
- Move(DecodedKey[32], Signature.Sig_s[0], 32);
- const LicenseBytes = Copy(DecodedKey, 64, MaxInt);
- const LicenseHash = SHA256Buf(LicenseBytes[0], ULength(LicenseBytes));
- var PublicKey: TECDSAPublickey;
- PublicKey.Public_x := ECDSAInt256FromString('76873a71a4d5cae3dfdb52f7e434582c25151e56338d6d7fd5423d1216dc3274');
- PublicKey.Public_y := ECDSAInt256FromString('4459f8d7c0e6c03e34806a4a4b949e0c16387fb8ff2f71d2d62ce6a29c713018');
- const ECDSAKey = TECDSAKey.Create;
- var Verified: Boolean;
- try
- ECDSAKey.ImportPublicKey(PublicKey);
- Verified := ECDSAKey.VerifySignature(LicenseHash, Signature);
- finally
- ECDSAKey.Free;
- end;
- if Verified then begin
- const LicenseString = TEncoding.UTF8.GetString(LicenseBytes);
- if LicenseString.StartsWith('1'#9) then begin
- const LicenseData = LicenseString.Split([#9]);
- if Length(LicenseData) = 4 then begin
- const LicenseeName = LicenseData[1];
- const LicenseType = LicenseData[2].ToInteger;
- var ExpirationDate: TDate;
- if TryDateFromDBDate(LicenseData[3], ExpirationDate) and (LicenseeName <> '') and
- (LicenseType >= Ord(Low(TLicenseType))) and (LicenseType <= Ord(High(TLicenseType))) then begin
- License.Key := CleanLicenseKey;
- License.Name := LicenseeName;
- License.Typ := TLicenseType(LicenseType);
- License.ExpirationDate := ExpirationDate;
- Result := True;
- end;
- end;
- end;
- end;
- end;
- end;
- end;
- end;
- function UpdateLicense(const LicenseKey: String): Boolean;
- begin
- if LicenseKey <> '' then
- Result := ParseLicenseKey(LicenseKey, License)
- else begin
- License := Default(TLicense);
- Result := True;
- end;
- end;
- procedure UpdateLicense(const ALicense: TLicense);
- begin
- License := ALicense;
- end;
- function IsLicensed: Boolean;
- begin
- Result := GetLicenseKey <> '';
- end;
- function GetLicenseKey: String;
- begin
- Result := License.Key;
- end;
- function GetChunkedLicenseKey: String;
- begin
- const Output = TStringList.Create;
- try
- var StartIndex := 1;
- const ChunkSize = 28;
- while StartIndex <= Length(License.Key) do begin
- Output.Add(Copy(License.Key, StartIndex, ChunkSize));
- StartIndex := StartIndex + ChunkSize;
- end;
- Result := Output.Text.Trim;
- finally
- Output.Free;
- end;
- end;
- var
- LinkerTimeStamp: TDateTime;
- ReadLinkerTimeStamp: Boolean;
- function GetLicenseState: TLicenseState;
- begin
- if not IsLicensed then
- Result := lsNotLicensed
- else if License.ExpirationDate <> 0 then begin
- if not ReadLinkerTimeStamp then begin
- try
- LinkerTimeStamp := PImageNtHeaders(HInstance + Cardinal(PImageDosHeader(HInstance)._lfanew)).FileHeader.TimeDateStamp / SecsPerDay + UnixDateDelta;
- except
- end;
- ReadLinkerTimeStamp := True;
- end;
- if (LinkerTimeStamp <> 0) and (LinkerTimeStamp > License.ExpirationDate) then
- Result := lsExpiredButUpdated
- else begin
- const CurrentDate = Date;
- if License.ExpirationDate < CurrentDate then
- Result := lsExpired
- else if License.ExpirationDate < IncMonth(CurrentDate, 1) then
- Result := lsExpiring
- else
- Result := lsLicensed;
- end;
- end else
- Result := lsLicensed;
- end;
- function GetLicenseeName: String;
- begin
- Result := License.Name;
- end;
- function GetLicenseeDescription: String;
- begin
- const LicenseState = GetLicenseState;
- if LicenseState <> lsNotLicensed then begin
- Result := GetLicenseeName;
- if LicenseState = lsExpired then
- Result := Result + ' (Update entitlement ended)'
- else if LicenseState = lsExpiredButUpdated then
- Result := Result + ' (Update entitlement ended but updated anyway)';
- end else
- Result := 'Non-commercial use only';
- end;
- function GetLicenseTypeDescription: String;
- begin
- case License.Typ of
- ltSingle: Result := 'Single User';
- ltTeam: Result := 'Team';
- ltEnterprise: Result := 'Enterprise';
- else
- raise Exception.Create('Unknown License.Typ');
- end;
- Result := 'Inno Setup ' + Result + ' License';
- end;
- function GetLicenseDescription(const Prefix, Separator: String): String;
- begin
- if IsLicensed then begin
- Result := Prefix + GetLicenseeName + ', ' + GetLicenseTypeDescription + '.' + Separator;
- if License.ExpirationDate <> 0 then
- Result := Result + 'Includes updates until ' + DateToStr(License.ExpirationDate) + ', major and minor.'
- else
- Result := Result + 'Includes all future updates, major and minor.';
- end else
- Result := GetLicenseeDescription + '.';
- end;
- end.
|