TrustFunc.pas 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  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. }
  9. {.$DEFINE TRUSTALL}
  10. interface
  11. procedure CheckFileTrust(const FileName: String; const CheckExists: Boolean = True);
  12. function LoadTrustedLibrary(const FileName: String; const TrustAllOnDebug: Boolean = False): HMODULE;
  13. implementation
  14. uses
  15. Winapi.Windows, System.SysUtils, System.Classes {$IFNDEF TRUSTALL}, ECDSA, SHA256, ISSigFunc {$ENDIF};
  16. procedure CheckFileTrust(const FileName: String; const CheckExists: Boolean);
  17. {$IFNDEF TRUSTALL}
  18. var
  19. AllowedKeys: array of TECDSAKey;
  20. {$ENDIF}
  21. begin
  22. if CheckExists then begin
  23. var Attr := GetFileAttributes(PChar(FileName));
  24. if (Attr = INVALID_FILE_ATTRIBUTES) or (Attr and faDirectory <> 0) then
  25. raise Exception.CreateFmt('File "%s" does not exist.',
  26. [FileName]);
  27. end;
  28. {$IFNDEF TRUSTALL}
  29. var AllowedPublicKey1Text, AllowedPublicKey2Text: String;
  30. {$I TrustFunc.AllowedPublicKeys.inc}
  31. var Key1: TECDSAKey := nil;
  32. var Key2: TECDSAKey := nil;
  33. try
  34. Key1 := TECDSAKey.Create;
  35. if ISSigImportKeyText(Key1, AllowedPublicKey1Text, False) <> ikrSuccess then
  36. raise Exception.Create('ISSigImportKeyText failed');
  37. if AllowedPublicKey2Text <> '' then begin
  38. Key2 := TECDSAKey.Create;
  39. if ISSigImportKeyText(Key2, AllowedPublicKey2Text, False) <> ikrSuccess then
  40. raise Exception.Create('ISSigImportKeyText failed');
  41. end;
  42. if Key2 <> nil then
  43. AllowedKeys := [Key1, Key2]
  44. else
  45. AllowedKeys := [Key1];
  46. const SigFileName = FileName + '.issig';
  47. const SigText = ISSigLoadTextFromFile(SigFileName);
  48. var ExpectedFileSize: Int64;
  49. var ExpectedFileHash: TSHA256Digest;
  50. if ISSigVerifySignatureText(AllowedKeys, SigText, ExpectedFileSize,
  51. ExpectedFileHash) <> vsrSuccess then
  52. raise Exception.CreateFmt('Signature file "%s" is not valid',
  53. [SigFileName]);
  54. const F = TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  55. try
  56. if F.Size <> ExpectedFileSize then
  57. raise Exception.CreateFmt('File "%s" is not trusted (incorrect size).',
  58. [FileName]);
  59. if not SHA256DigestsEqual(ISSigCalcStreamHash(F), ExpectedFileHash) then
  60. raise Exception.CreateFmt('File "%s" is not trusted (incorrect hash).',
  61. [FileName]);
  62. finally
  63. F.Free;
  64. end;
  65. finally
  66. Key2.Free;
  67. Key1.Free;
  68. end;
  69. {$ENDIF}
  70. end;
  71. function Win32ErrorString(ErrorCode: Integer): String;
  72. { Like SysErrorMessage but also passes the FORMAT_MESSAGE_IGNORE_INSERTS flag
  73. which allows the function to succeed on errors like 129 }
  74. var
  75. Len: Integer;
  76. Buffer: array[0..1023] of Char;
  77. begin
  78. Len := FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM or
  79. FORMAT_MESSAGE_IGNORE_INSERTS or FORMAT_MESSAGE_ARGUMENT_ARRAY, nil,
  80. ErrorCode, 0, Buffer, SizeOf(Buffer) div SizeOf(Buffer[0]), nil);
  81. while (Len > 0) and ((Buffer[Len-1] <= ' ') or (Buffer[Len-1] = '.')) do
  82. Dec(Len);
  83. SetString(Result, Buffer, Len);
  84. end;
  85. function DoLoadLibrary(const FileName: String): HMODULE;
  86. begin
  87. Result := SafeLoadLibrary(PChar(FileName), SEM_NOOPENFILEERRORBOX);
  88. if Result = 0 then
  89. raise Exception.Create(Win32ErrorString(GetLastError));
  90. end;
  91. function LoadTrustedLibrary(const FileName: String; const TrustAllOnDebug: Boolean): HMODULE;
  92. begin
  93. {$IFDEF DEBUG}
  94. if TrustAllOnDebug then begin
  95. Result := DoLoadLibrary(FileName);
  96. Exit;
  97. end;
  98. {$ENDIF}
  99. { First open a temporary regular handle to the library to protect it from changes
  100. between the trust check and the load }
  101. const F = TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  102. try
  103. CheckFileTrust(FileName, False);
  104. Result := DoLoadLibrary(FileName);
  105. finally
  106. F.Free;
  107. end;
  108. end;
  109. end.