TrustFunc.pas 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  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 Compil32, 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: Integer): 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. Len: Integer;
  31. Buffer: array[0..1023] of Char;
  32. begin
  33. Len := FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM or
  34. FORMAT_MESSAGE_IGNORE_INSERTS or FORMAT_MESSAGE_ARGUMENT_ARRAY, nil,
  35. ErrorCode, 0, Buffer, SizeOf(Buffer) div SizeOf(Buffer[0]), nil);
  36. while (Len > 0) and ((Buffer[Len-1] <= ' ') or (Buffer[Len-1] = '.')) do
  37. Dec(Len);
  38. SetString(Result, Buffer, Len);
  39. end;
  40. function CheckFileTrust(const Filename: String; const Options: TCheckFileTrustOptions): TFileStream;
  41. {$IFNDEF TRUSTALL}
  42. var
  43. AllowedKeys: array of TECDSAKey;
  44. {$ENDIF}
  45. begin
  46. var Attr := GetFileAttributes(PChar(Filename));
  47. if (Attr = INVALID_FILE_ATTRIBUTES) or (Attr and faDirectory <> 0) then
  48. raise Exception.Create(Win32ErrorString(ERROR_FILE_NOT_FOUND));
  49. {$IFNDEF TRUSTALL}
  50. {$IFDEF DEBUG}
  51. if cftoTrustAllOnDebug in Options then
  52. Exit(nil);
  53. {$ENDIF}
  54. var ExpectedFileName: String;
  55. var ExpectedFileSize: Int64;
  56. var ExpectedFileHash: TSHA256Digest;
  57. var AllowedPublicKey1Text, AllowedPublicKey2Text: String;
  58. {$I TrustFunc.AllowedPublicKeys.inc}
  59. var Key1: TECDSAKey := nil;
  60. var Key2: TECDSAKey := nil;
  61. try
  62. { Import keys }
  63. Key1 := TECDSAKey.Create;
  64. if ISSigImportKeyText(Key1, AllowedPublicKey1Text, False) <> ikrSuccess then
  65. raise Exception.Create('ISSigImportKeyText failed');
  66. if AllowedPublicKey2Text <> '' then begin
  67. Key2 := TECDSAKey.Create;
  68. if ISSigImportKeyText(Key2, AllowedPublicKey2Text, False) <> ikrSuccess then
  69. raise Exception.Create('ISSigImportKeyText failed');
  70. end;
  71. if Key2 <> nil then
  72. AllowedKeys := [Key1, Key2]
  73. else
  74. AllowedKeys := [Key1];
  75. { Verify signature }
  76. if not ISSigVerifySignature(Filename, AllowedKeys, ExpectedFileName, ExpectedFileSize, ExpectedFileHash,
  77. nil,
  78. procedure(const Filename, SigFilename: String)
  79. begin
  80. raise Exception.CreateFmt('Signature file "%s" does not exist', [SigFilename]);
  81. end,
  82. procedure(const Filename, SigFilename: String; const VerifyResult: TISSigVerifySignatureResult)
  83. begin
  84. raise Exception.CreateFmt('Signature file "%s" is not valid', [SigFilename]);
  85. end
  86. ) then
  87. raise Exception.Create('Unexpected ISSigVerifySignature result');
  88. finally
  89. Key2.Free;
  90. Key1.Free;
  91. end;
  92. { Verify file, keeping open afterwards if requested
  93. Also see Setup.ScriptFunc's ISSigVerify which can also keep open afterwards }
  94. if (ExpectedFileName <> '') and not PathSame(PathExtractName(Filename), ExpectedFileName) then
  95. raise Exception.CreateFmt('File "%s" is not trusted (incorrect name).', [Filename]);
  96. var F := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
  97. try
  98. if F.Size <> ExpectedFileSize then
  99. raise Exception.CreateFmt('File "%s" is not trusted (incorrect size).', [Filename]);
  100. if not SHA256DigestsEqual(ISSigCalcStreamHash(F), ExpectedFileHash) then
  101. raise Exception.CreateFmt('File "%s" is not trusted (incorrect hash).', [Filename]);
  102. except
  103. FreeAndNil(F);
  104. raise;
  105. end;
  106. if not (cftoKeepOpen in Options) then
  107. FreeAndNil(F);
  108. Result := F;
  109. {$ELSE}
  110. Result := nil;
  111. {$ENDIF}
  112. end;
  113. function DoLoadLibrary(const Filename: String): HMODULE;
  114. begin
  115. Result := SafeLoadLibrary(PChar(Filename), SEM_NOOPENFILEERRORBOX);
  116. if Result = 0 then
  117. raise Exception.Create(Win32ErrorString(GetLastError));
  118. end;
  119. function LoadTrustedLibrary(const Filename: String; const Options: TLoadTrustedLibraryOptions): HMODULE;
  120. begin
  121. var CheckFileTrustOptions: TCheckFileTrustOptions := [cftoKeepOpen];
  122. if ltloTrustAllOnDebug in Options then
  123. Include(CheckFileTrustOptions, cftoTrustAllOnDebug);
  124. const F = CheckFileTrust(Filename, CheckFileTrustOptions);
  125. try
  126. Result := DoLoadLibrary(Filename);
  127. finally
  128. F.Free;
  129. end;
  130. end;
  131. end.