TrustFunc.pas 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. unit TrustFunc;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Trust support functions using ISSigFunc and key texts from TrustFunc.AllowedPublicKeys.inc
  8. In Inno Setup these functions are only used by ISIDE, ISCC, and ISCmplr. Verification of
  9. the user's files by ISCmplr and Setup is done by calling ISSigFunc directly and uses the
  10. user's key texts.
  11. }
  12. {.$DEFINE TRUSTALL}
  13. interface
  14. uses
  15. System.Classes;
  16. type
  17. TCheckFileTrustOption = (cftoKeepOpen, cftoTrustAllOnDebug);
  18. TCheckFileTrustOptions = set of TCheckFileTrustOption;
  19. TLoadTrustedLibraryOption = (ltloTrustAllOnDebug);
  20. TLoadTrustedLibraryOptions = set of TLoadTrustedLibraryOption;
  21. function CheckFileTrust(const Filename: String; const Options: TCheckFileTrustOptions): TFileStream;
  22. function LoadTrustedLibrary(const Filename: String; const Options: TLoadTrustedLibraryOptions): HMODULE;
  23. implementation
  24. uses
  25. Winapi.Windows, System.SysUtils {$IFNDEF TRUSTALL}, ECDSA, SHA256, ISSigFunc, PathFunc {$ENDIF};
  26. function Win32ErrorString(ErrorCode: Cardinal): String;
  27. { Like SysErrorMessage but also passes the FORMAT_MESSAGE_IGNORE_INSERTS flag
  28. which allows the function to succeed on errors like 129 }
  29. var
  30. Buffer: array[0..1023] of Char;
  31. begin
  32. var Len := FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM or
  33. FORMAT_MESSAGE_IGNORE_INSERTS or FORMAT_MESSAGE_ARGUMENT_ARRAY, nil,
  34. ErrorCode, 0, Buffer, SizeOf(Buffer) div SizeOf(Buffer[0]), nil);
  35. while (Len > 0) and ((Buffer[Len-1] <= ' ') or (Buffer[Len-1] = '.')) do
  36. Dec(Len);
  37. SetString(Result, Buffer, Len);
  38. end;
  39. function CheckFileTrust(const Filename: String; const Options: TCheckFileTrustOptions): TFileStream;
  40. {$IFNDEF TRUSTALL}
  41. var
  42. AllowedKeys: array of TECDSAKey;
  43. {$ENDIF}
  44. begin
  45. var Attr := GetFileAttributes(PChar(Filename));
  46. if (Attr = INVALID_FILE_ATTRIBUTES) or (Attr and faDirectory <> 0) then
  47. raise Exception.Create(Win32ErrorString(ERROR_FILE_NOT_FOUND));
  48. {$IFNDEF TRUSTALL}
  49. {$IFDEF DEBUG}
  50. if cftoTrustAllOnDebug in Options then
  51. Exit(nil);
  52. {$ENDIF}
  53. var ExpectedFileName: String;
  54. var ExpectedFileSize: Int64;
  55. var ExpectedFileHash: TSHA256Digest;
  56. var AllowedPublicKey1Text, AllowedPublicKey2Text: String;
  57. {$I TrustFunc.AllowedPublicKeys.inc}
  58. var Key1: TECDSAKey := nil;
  59. var Key2: TECDSAKey := nil;
  60. try
  61. { Import keys }
  62. Key1 := TECDSAKey.Create;
  63. if ISSigImportKeyText(Key1, AllowedPublicKey1Text, False) <> ikrSuccess then
  64. raise Exception.Create('ISSigImportKeyText failed');
  65. if AllowedPublicKey2Text <> '' then begin
  66. Key2 := TECDSAKey.Create;
  67. if ISSigImportKeyText(Key2, AllowedPublicKey2Text, False) <> ikrSuccess then
  68. raise Exception.Create('ISSigImportKeyText failed');
  69. end;
  70. if Key2 <> nil then
  71. AllowedKeys := [Key1, Key2]
  72. else
  73. AllowedKeys := [Key1];
  74. { Verify signature }
  75. if not ISSigVerifySignature(Filename, AllowedKeys, ExpectedFileName, ExpectedFileSize, ExpectedFileHash,
  76. nil,
  77. procedure(const Filename, SigFilename: String)
  78. begin
  79. raise Exception.CreateFmt('Signature file "%s" does not exist', [SigFilename]);
  80. end,
  81. procedure(const Filename, SigFilename: String; const VerifyResult: TISSigVerifySignatureResult)
  82. begin
  83. raise Exception.CreateFmt('Signature file "%s" is not valid', [SigFilename]);
  84. end
  85. ) then
  86. raise Exception.Create('Unexpected ISSigVerifySignature result');
  87. finally
  88. Key2.Free;
  89. Key1.Free;
  90. end;
  91. { Verify file, keeping open afterwards if requested
  92. Also see Setup.ScriptFunc's ISSigVerify which can also keep open afterwards }
  93. if (ExpectedFileName <> '') and not PathSame(PathExtractName(Filename), ExpectedFileName) then
  94. raise Exception.CreateFmt('File "%s" is not trusted (incorrect name).', [Filename]);
  95. var F := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
  96. try
  97. if F.Size <> ExpectedFileSize then
  98. raise Exception.CreateFmt('File "%s" is not trusted (incorrect size).', [Filename]);
  99. if not SHA256DigestsEqual(ISSigCalcStreamHash(F), ExpectedFileHash) then
  100. raise Exception.CreateFmt('File "%s" is not trusted (incorrect hash).', [Filename]);
  101. except
  102. FreeAndNil(F);
  103. raise;
  104. end;
  105. if not (cftoKeepOpen in Options) then
  106. FreeAndNil(F);
  107. Result := F;
  108. {$ELSE}
  109. Result := nil;
  110. {$ENDIF}
  111. end;
  112. function DoLoadLibrary(const Filename: String): HMODULE;
  113. begin
  114. Result := SafeLoadLibrary(PChar(Filename), SEM_NOOPENFILEERRORBOX);
  115. if Result = 0 then
  116. raise Exception.Create(Win32ErrorString(GetLastError));
  117. end;
  118. function LoadTrustedLibrary(const Filename: String; const Options: TLoadTrustedLibraryOptions): HMODULE;
  119. begin
  120. var CheckFileTrustOptions: TCheckFileTrustOptions := [cftoKeepOpen];
  121. if ltloTrustAllOnDebug in Options then
  122. Include(CheckFileTrustOptions, cftoTrustAllOnDebug);
  123. const F = CheckFileTrust(Filename, CheckFileTrustOptions);
  124. try
  125. Result := DoLoadLibrary(Filename);
  126. finally
  127. F.Free;
  128. end;
  129. end;
  130. end.