소스 검색

Merge branch 'files-hash'

Martijn Laan 3 달 전
부모
커밋
6e32515c60

+ 5 - 6
Examples/CodeDownloadFiles.iss

@@ -7,10 +7,10 @@
 ; -For innosetup-latest.exe and MyProg-ExtraReadmes.7z: using Inno Setup
 ;  Signature Tool, the [ISSigKeys] section, and the AddWithISSigVerify support
 ;  function
-; -For iscrypt.dll: using a simple SHA256 check
+; -For iscrypt.dll: using a simple SHA-256 hash check
 ; Using the Inno Setup Signature Tool has the benefit that the script does not
 ; need to be changed when the downloaded file changes, so any installers built
-; will also keep working
+; will also keep working (they are "evergreen")
 
 [Setup]
 AppName=My Program
@@ -38,10 +38,12 @@ Source: "Readme.txt"; DestDir: "{app}"; Flags: isreadme
 ; These files will be downloaded using [Files] only
 Source: "https://jrsoftware.org/download.php/is.exe?dontcount=1"; DestName: "innosetup-latest.exe"; DestDir: "{app}"; \
   ExternalSize: 7_000_000; Flags: external download ignoreversion issigverify
+Source: "https://jrsoftware.org/download.php/iscrypt.dll?dontcount=1"; DestName: "ISCrypt.dll"; DestDir: "{app}"; \
+  Hash: "2f6294f9aa09f59a574b5dcd33be54e16b39377984f3d5658cda44950fa0f8fc"; \
+  ExternalSize: 2560; Flags: external download ignoreversion
 ; These files will be downloaded by [Code]. If you include flag issigverify here the file will be verified
 ; a second time while copying. Verification while copying is efficient, except for archives.
 Source: "{tmp}\MyProg-ExtraReadmes.7z"; DestDir: "{app}"; Flags: external extractarchive recursesubdirs ignoreversion
-Source: "{tmp}\ISCrypt.dll"; DestDir: "{app}"; Flags: external ignoreversion
 
 [Icons]
 Name: "{group}\My Program"; Filename: "{app}\MyProg.exe"
@@ -75,9 +77,6 @@ begin
     DownloadPage.AddWithISSigVerify(
       'https://jrsoftware.org/download.php/myprog-extrareadmes.7z', '',
       'MyProg-ExtraReadmes.7z', AllowedKeysRuntimeIDs);
-    DownloadPage.Add(
-      'https://jrsoftware.org/download.php/iscrypt.dll?dontcount=1',
-      'ISCrypt.dll', '2f6294f9aa09f59a574b5dcd33be54e16b39377984f3d5658cda44950fa0f8fc');
     DownloadPage.Show;
     try
       try

+ 0 - 2
Files/Default.isl

@@ -219,8 +219,6 @@ StopDownload=Are you sure you want to stop the download?
 ErrorDownloadAborted=Download aborted
 ErrorDownloadFailed=Download failed: %1 %2
 ErrorDownloadSizeFailed=Getting size failed: %1 %2
-ErrorFileHash1=File hash failed: %1
-ErrorFileHash2=Invalid file hash: expected %1, found %2
 ErrorProgress=Invalid progress: %1 of %2
 ErrorFileSize=Invalid file size: expected %1, found %2
 

+ 8 - 1
ISHelp/isetup.xml

@@ -1665,6 +1665,13 @@ ExternalSize: 1_048_576; Flags: external
 </example>
 </param>
 
+<param name="Hash">
+<p>Instructs the compiler or Setup to do a simple SHA-256 hash check instead of a full signature verification, as an alternative to using the <tt>issigverify</tt> flag. The precise effect of this flag depends on whether it is combined with the <tt>external</tt> flag. See the <tt>issigverify</tt> flag description for more information.</p>
+<example>
+<pre>Hash: "2f6294f9aa09f59a574b5dcd33be54e16b39377984f3d5658cda44950fa0f8fc"</pre>
+</example>
+</param>
+
 <param name="ExtractArchivePassword">
 <p>Specifies the password of the archive, which can include constants. Please be aware that this password is stored in an unencrypted form in the resulting Setup file(s), even if you have enabled encryption (using the [Setup] section directive <tt>Encryption</tt>).</p>
 <p>This parameter is ignored if the <tt>extractarchive</tt> flag isn't also specified.</p>
@@ -3709,7 +3716,7 @@ Source: "MyFile.txt"; DestDir: "{app}"; Flags: issigverify
 <p>Compile the script. In the compiler output, you should see a line indicating the file was successfully verified:</p>
 <precode>
    Compressing: MyFile.txt
-      ISSig verification successful.
+      Verification successful.
 </precode>
 </li>
 

+ 6 - 5
Projects/Src/Compiler.Messages.pas

@@ -53,7 +53,7 @@ const
   SCompilerStatusFilesCompressingVersion = '   Compressing: %s   (%u.%u.%u.%u)';
   SCompilerStatusFilesStoring = '   Storing: %s';
   SCompilerStatusFilesStoringVersion = '   Storing: %s   (%u.%u.%u.%u)';
-  SCompilerStatusFilesISSigVerified = '      ISSig verification successful.';
+  SCompilerStatusFilesVerified = '      Verification successful.';
   SCompilerStatusCompressingSetupExe = '   Compressing Setup program executable';
   SCompilerStatusUpdatingVersionInfo = '   Updating version info (%s)';
   SCompilerStatusUpdatingManifest = '   Updating manifest (%s)';
@@ -218,7 +218,7 @@ const
   { Flags }
   SCompilerParamUnknownFlag2 = 'Parameter "%s" includes an unknown flag';
   SCompilerParamErrorBadCombo2 = 'Parameter "%s" cannot have both the "%s" and "%s" flags';
-  SCompilerParamErrorBadCombo3 = 'Parameter "%s" cannot have both the "%s" and "%s" flags on the same source file';
+  SCompilerParamErrorBadCombo2SameSource = 'Parameter "%s" cannot have both the "%s" and "%s" flags on a single source file';
   SCompilerParamUnsupportedFlag = 'Parameter "%s" includes a flag that is not supported in this section';
   SCompilerParamFlagMissing = 'Flag "%s" must be used if flag "%s" is used';
   SCompilerParamFlagMissing2 = 'Flag "%s" must be used if parameter "%s" is used';
@@ -291,8 +291,9 @@ const
   SCompilerFilesWildcardNotMatched = 'No files found matching "%s"';
   SCompilerFilesDestNameCantBeSpecified = 'Parameter "DestName" cannot be specified if ' +
     'the "Source" parameter contains wildcards or flag "extractarchive" is used';
-  SCompilerFilesCantHaveNonExternalExternalSize = 'Parameter "ExternalSize" may only be used when ' +
-    'the "external" flag is used';
+  SCompilerFilesParamRequiresFlag = 'Parameter "%s" may only be used when the "%s" flag is used';
+  SCompilerFilesParamFlagConflict = 'Parameter "%s" may not be used when the "%s" flag is used';
+  SCompilerFilesParamFlagConflictSameSource = 'Parameter "%s" and the "%s" flag cannot both be used on a single source file';
   SCompilerFilesExcludeTooLong = 'Parameter "Excludes" contains a pattern that is too long';
   SCompilerFilesUnsafeFile = 'Unsafe file detected: %s.' + SNewLine2 +
     'See the "Unsafe Files" topic in the help file for more information';
@@ -312,7 +313,7 @@ const
     'documentation in the help file for details.';
   SCompilerFilesISSigVerifyMissingISSigKeys = 'Flag "issigverify" may not be used when the "ISSigKeys" section doesn''t exist or is empty.';
   SCompilerFilesISSigAllowedKeysMissingISSigVerify = 'Flag "issigverify" must be used when the "ISSigAllowedKeys" parameter is used.';
-  SCompilerFilesISSigAllowedKeysConflict = 'Parameter "ISSigAllowedKeys" cannot allow different keys on the same source file';
+  SCompilerFilesValueConflict = 'Parameter "%s" cannot allow different values on the same source file';
   SCompilerFilesUnkownISSigKeyNameOrGroupName = 'Parameter "%s" includes an unknown name or group name.';
 
   { [Icons] }

+ 63 - 28
Projects/Src/Compiler.SetupCompiler.pas

@@ -256,7 +256,7 @@ type
     procedure WriteCompiledCodeDebugInfo(const CompiledCodeDebugInfo: AnsiString);
     function CreateMemoryStreamsFromFiles(const ADirectiveName, AFiles: String): TObjectList<TCustomMemoryStream>;
     function CreateMemoryStreamsFromResources(const AResourceNamesPrefixes, AResourceNamesPostfixes: array of String): TObjectList<TCustomMemoryStream>;
-    procedure ISSigVerifyError(const AError: TISSigVerifySignatureError;
+    procedure VerificationError(const AError: TVerificationError;
       const AFilename: String; const ASigFilename: String = '');
   public
     AppData: Longint;
@@ -324,9 +324,9 @@ type
   PFileLocationEntryExtraInfo = ^TFileLocationEntryExtraInfo;
   TFileLocationEntryExtraInfo = record
     Flags: set of (floVersionInfoNotValid, floIsUninstExe, floApplyTouchDateTime,
-      floSolidBreak, floISSigVerify);
+      floSolidBreak);
     Sign: TFileLocationSign;
-    ISSigAllowedKeys: AnsiString;
+    Verification: TSetupFileVerification;
     ISSigKeyUsedID: String;
   end;
 
@@ -4668,7 +4668,7 @@ procedure TSetupCompiler.EnumFilesProc(const Line: PChar; const Ext: Integer);
 type
   TParam = (paFlags, paSource, paDestDir, paDestName, paCopyMode, paAttribs,
     paPermissions, paFontInstall, paExcludes, paExternalSize, paExtractArchivePassword,
-    paStrongAssemblyName, paISSigAllowedKeys, paDownloadISSigSource, paDownloadUserName,
+    paStrongAssemblyName, paHash, paISSigAllowedKeys, paDownloadISSigSource, paDownloadUserName,
     paDownloadPassword, paComponents, paTasks, paLanguages, paCheck, paBeforeInstall,
     paAfterInstall, paMinVersion, paOnlyBelowVersion);
 const
@@ -4683,6 +4683,7 @@ const
   ParamFilesExternalSize = 'ExternalSize';
   ParamFilesExtractArchivePassword = 'ExtractArchivePassword';
   ParamFilesStrongAssemblyName = 'StrongAssemblyName';
+  ParamFilesHash = 'Hash';
   ParamFilesISSigAllowedKeys = 'ISSigAllowedKeys';
   ParamFilesDownloadISSigSource = 'DownloadISSigSource';
   ParamFilesDownloadUserName = 'DownloadUserName';
@@ -4700,6 +4701,7 @@ const
     (Name: ParamFilesExternalSize; Flags: []),
     (Name: ParamFilesExtractArchivePassword; Flags: []),
     (Name: ParamFilesStrongAssemblyName; Flags: [piNoEmpty]),
+    (Name: ParamFilesHash; Flags: [piNoEmpty]),
     (Name: ParamFilesISSigAllowedKeys; Flags: [piNoEmpty]),
     (Name: ParamFilesDownloadISSigSource; Flags: []),
     (Name: ParamFilesDownloadUserName; Flags: [piNoEmpty]),
@@ -4908,6 +4910,15 @@ type
       Sign := NewSign;
   end;
 
+  procedure ApplyNewVerificationType(var VerificationType: TSetupFileVerificationType;
+    const NewVerificationType: TSetupFileVerificationType; const ErrorMessage: String);
+  begin
+    if not (VerificationType in [fvNone, NewVerificationType]) then
+       AbortCompileFmt(ErrorMessage, ['Hash', 'issigverify'])
+    else
+      VerificationType := NewVerificationType;
+  end;
+
   procedure ProcessFileList(const FileListBaseDir: String; FileList: TList);
   var
     FileListRec: PFileListRec;
@@ -4982,13 +4993,23 @@ type
               to compressing the first one }
             SolidBreak := False;
           end;
-          NewFileLocationEntryExtraInfo^.ISSigAllowedKeys := NewFileEntry^.ISSigAllowedKeys;
-        end else if NewFileLocationEntryExtraInfo^.ISSigAllowedKeys <> NewFileEntry^.ISSigAllowedKeys then
-          AbortCompile(SCompilerFilesISSigAllowedKeysConflict);
+          NewFileLocationEntryExtraInfo^.Verification.Typ := fvNone; { Correct value set below }
+          NewFileLocationEntryExtraInfo^.Verification.Hash := NewFileEntry^.Verification.Hash;
+          NewFileLocationEntryExtraInfo^.Verification.ISSigAllowedKeys := NewFileEntry^.Verification.ISSigAllowedKeys;
+        end else begin
+          { Verification.Typ changes checked below }
+          if (NewFileLocationEntryExtraInfo^.Verification.Typ = fvHash) and
+             (NewFileEntry^.Verification.Typ = fvHash) and
+             not CompareMem(@NewFileLocationEntryExtraInfo^.Verification.Hash[0],
+               @NewFileEntry^.Verification.Hash[0], SizeOf(TSHA256Digest)) then
+            AbortCompileFmt(SCompilerFilesValueConflict, ['Hash']);
+          if (NewFileLocationEntryExtraInfo^.Verification.Typ = fvISSig) and
+             (NewFileEntry^.Verification.Typ = fvISSig) and
+             (NewFileLocationEntryExtraInfo^.Verification.ISSigAllowedKeys <> NewFileEntry^.Verification.ISSigAllowedKeys) then
+            AbortCompileFmt(SCompilerFilesValueConflict, ['ISSigAllowedKeys']);
+        end;
         if Touch then
           Include(NewFileLocationEntryExtraInfo^.Flags, floApplyTouchDateTime);
-        if foISSigVerify in NewFileEntry^.Options then
-          Include(NewFileLocationEntryExtraInfo^.Flags, floISSigVerify);
         { Note: "nocompression"/"noencryption" on one file makes all merged
           copies uncompressed/unencrypted too }
         if NoCompression then
@@ -4996,7 +5017,10 @@ type
         if NoEncryption then
           Exclude(NewFileLocationEntry^.Flags, floChunkEncrypted);
         if Sign <> fsNoSetting then
-          ApplyNewSign(NewFileLocationEntryExtraInfo.Sign, Sign, SCompilerParamErrorBadCombo3);
+          ApplyNewSign(NewFileLocationEntryExtraInfo.Sign, Sign, SCompilerParamErrorBadCombo2SameSource);
+        if NewFileEntry^.Verification.Typ <> fvNone  then
+          ApplyNewVerificationType(NewFileLocationEntryExtraInfo.Verification.Typ, NewFileEntry^.Verification.Typ,
+            SCompilerFilesParamFlagConflictSameSource);
       end
       else begin
         NewFileEntry^.SourceFilename := SourceFile;
@@ -5263,7 +5287,7 @@ begin
                    38: ApplyNewSign(Sign, fsYes, SCompilerParamErrorBadCombo2);
                    39: ApplyNewSign(Sign, fsOnce, SCompilerParamErrorBadCombo2);
                    40: ApplyNewSign(Sign, fsCheck, SCompilerParamErrorBadCombo2);
-                   41: Include(Options, foISSigVerify);
+                   41: ApplyNewVerificationType(Verification.Typ, fvISSig, SCompilerFilesParamFlagConflict);
                    42: Include(Options, foDownload);
                    43: Include(Options, foExtractArchive);
                  end;
@@ -5345,7 +5369,7 @@ begin
                { ExternalSize }
                if Values[paExternalSize].Found then begin
                  if not ExternalFile then
-                   AbortCompile(SCompilerFilesCantHaveNonExternalExternalSize);
+                   AbortCompileFmt(SCompilerFilesParamRequiresFlag, ['ExternalSize', 'external']);
                  if not StrToInteger64(Values[paExternalSize].Data, ExternalSize) then
                    AbortCompileParamError(SCompilerParamInvalid2, ParamFilesExternalSize);
                  Include(Options, foExternalSizePreset);
@@ -5363,6 +5387,12 @@ begin
                { ExtractArchivePassword }
                ExtractArchivePassword := Values[paExtractArchivePassword].Data;
 
+               { Hash }
+               if Values[paHash].Found then begin
+                 ApplyNewVerificationType(Verification.Typ, fvHash, SCompilerFilesParamFlagConflict);
+                 Verification.Hash := SHA256DigestFromString(Values[paHash].Data);
+               end;
+
                { ISSigAllowedKeys }
                var S := Values[paISSigAllowedKeys].Data;
                while True do begin
@@ -5374,7 +5404,7 @@ begin
                    var ISSigKeyEntryExtraInfo := PISSigKeyEntryExtraInfo(ISSigKeyEntryExtraInfos[KeyIndex]);
                    if SameText(ISSigKeyEntryExtraInfo.Name, KeyNameOrGroupName) or
                       ISSigKeyEntryExtraInfo.HasGroupName(KeyNameOrGroupName) then begin
-                     SetISSigAllowedKey(ISSigAllowedKeys, KeyIndex);
+                     SetISSigAllowedKey(Verification.ISSigAllowedKeys, KeyIndex);
                      FoundKey := True;
                    end;
                  end;
@@ -5478,13 +5508,16 @@ begin
         if (foIgnoreVersion in Options) and (foReplaceSameVersionIfContentsDiffer in Options) then
           AbortCompileFmt(SCompilerParamErrorBadCombo2, ['Flags', 'ignoreversion', 'replacesameversion']);
 
-        if (ISSigKeyEntries.Count = 0) and (foISSigVerify in Options) then
+        if (ISSigKeyEntries.Count = 0) and (Verification.Typ = fvISSig) then
           AbortCompile(SCompilerFilesISSigVerifyMissingISSigKeys);
-        if (ISSigAllowedKeys <> '') and not (foISSigVerify in Options) then
+        if (Verification.ISSigAllowedKeys <> '') and (Verification.Typ <> fvISSig) then
           AbortCompile(SCompilerFilesISSigAllowedKeysMissingISSigVerify);
 
         if Sign in [fsYes, fsOnce] then begin
-          if foISSigVerify in Options then
+          if Verification.Typ = fvHash then
+            AbortCompileFmt(SCompilerFilesParamFlagConflict,
+              [ParamCommonFlags, 'Hash', SignFlags[Sign]]);
+          if Verification.Typ = fvISSig then
             AbortCompileFmt(SCompilerParamErrorBadCombo2,
               [ParamCommonFlags, SignFlags[Sign], 'issigverify']);
           if SignTools.Count = 0 then
@@ -6643,10 +6676,10 @@ begin
   end;
 end;
 
-procedure TSetupCompiler.ISSigVerifyError(const AError: TISSigVerifySignatureError;
+procedure TSetupCompiler.VerificationError(const AError: TVerificationError;
   const AFilename, ASigFilename: String);
 const
-  Messages: array[TISSigVerifySignatureError] of String =
+  Messages: array[TVerificationError] of String =
     (SCompilerVerificationSignatureDoesntExist, SCompilerVerificationSignatureMalformed,
      SCompilerVerificationKeyNotFound, SCompilerVerificationSignatureBad,
      SCompilerVerificationFileSizeIncorrect, SCompilerVerificationFileHashIncorrect);
@@ -7149,26 +7182,28 @@ var
           fdOpenExisting, faRead, fsRead);
         try
           var ExpectedFileHash: TSHA256Digest;
-          if floISSigVerify in FLExtraInfo.Flags then begin
+          if FLExtraInfo.Verification.Typ = fvHash then
+            ExpectedFileHash := FLExtraInfo.Verification.Hash
+          else if FLExtraInfo.Verification.Typ = fvISSig then begin
             { See Setup.Install's CopySourceFileToDestFile for similar code }
             if Length(ISSigAvailableKeys) = 0 then { shouldn't fail: flag stripped already }
               AbortCompileFmt(SCompilerCompressInternalError, ['Length(ISSigAvailableKeys) = 0']);
             var ExpectedFileSize: Int64;
             if not ISSigVerifySignature(FileLocationEntryFilenames[I],
-              GetISSigAllowedKeys(ISSigAvailableKeys, FLExtraInfo.ISSigAllowedKeys),
+              GetISSigAllowedKeys(ISSigAvailableKeys, FLExtraInfo.Verification.ISSigAllowedKeys),
               ExpectedFileSize, ExpectedFileHash, FLExtraInfo.ISSigKeyUsedID,
               nil,
               procedure(const Filename, SigFilename: String)
               begin
-                ISSigVerifyError(vseSignatureMissing, Filename, SigFilename);
+                VerificationError(veSignatureMissing, Filename, SigFilename);
               end,
               procedure(const Filename, SigFilename: String; const VerifyResult: TISSigVerifySignatureResult)
               begin
                 var VerifyResultAsString: String;
                 case VerifyResult of
-                  vsrMalformed: ISSigVerifyError(vseSignatureMalformed, Filename, SigFilename);
-                  vsrBad: ISSigVerifyError(vseSignatureBad, Filename, SigFilename);
-                  vsrKeyNotFound: ISSigVerifyError(vseKeyNotFound, Filename, SigFilename);
+                  vsrMalformed: VerificationError(veSignatureMalformed, Filename, SigFilename);
+                  vsrBad: VerificationError(veSignatureBad, Filename, SigFilename);
+                  vsrKeyNotFound: VerificationError(veKeyNotFound, Filename, SigFilename);
                 else
                   AbortCompileFmt(SCompilerCompressInternalError, ['Unknown ISSigVerifySignature result'])
                 end;
@@ -7176,7 +7211,7 @@ var
             ) then
               AbortCompileFmt(SCompilerCompressInternalError, ['Unexpected ISSigVerifySignature result']);
             if Int64(SourceFile.Size) <> ExpectedFileSize then
-              ISSigVerifyError(vseFileSizeIncorrect, FileLocationEntryFilenames[I]);
+              VerificationError(veFileSizeIncorrect, FileLocationEntryFilenames[I]);
             { ExpectedFileHash checked below after compression }
           end;
 
@@ -7226,10 +7261,10 @@ var
           CH.CompressFile(SourceFile, FL.OriginalSize,
             floCallInstructionOptimized in FL.Flags, FL.SHA256Sum);
 
-          if floISSigVerify in FLExtraInfo.Flags then begin
+          if FLExtraInfo.Verification.Typ <> fvNone then begin
             if not SHA256DigestsEqual(FL.SHA256Sum, ExpectedFileHash) then
-              ISSigVerifyError(vseFileHashIncorrect, FileLocationEntryFilenames[I]);
-            AddStatus(SCompilerStatusFilesISSigVerified);
+              VerificationError(veFileHashIncorrect, FileLocationEntryFilenames[I]);
+            AddStatus(SCompilerStatusFilesVerified);
           end;
         finally
           SourceFile.Free;

+ 1 - 1
Projects/Src/IDE.ScintStylerInnoSetup.pas

@@ -245,7 +245,7 @@ const
     'AfterInstall', 'Attribs', 'BeforeInstall', 'Check', 'Components', 'CopyMode',
     'DestDir', 'DestName', 'DownloadISSigSource', 'DownloadPassword',
     'DownloadUserName', 'Excludes', 'ExternalSize', 'ExtractArchivePassword',
-    'Flags', 'FontInstall', 'ISSigAllowedKeys', 'Languages', 'MinVersion',
+    'Flags', 'FontInstall', 'Hash', 'ISSigAllowedKeys', 'Languages', 'MinVersion',
     'OnlyBelowVersion', 'Permissions', 'Source', 'StrongAssemblyName', 'Tasks'
   ];
 

+ 86 - 76
Projects/Src/Setup.Install.pas

@@ -12,9 +12,11 @@ unit Setup.Install;
 interface
 
 uses
-  Classes, SHA256, Shared.FileClass, Shared.SetupTypes, Shared.Int64Em;
+  Classes, SHA256, Shared.FileClass, Shared.SetupTypes, Shared.Int64Em, Shared.Struct;
 
-procedure ISSigVerifyError(const AError: TISSigVerifySignatureError;
+function NoVerification: TSetupFileVerification;
+
+procedure VerificationError(const AError: TVerificationError;
   const ASigFilename: String = '');
 
 procedure DoISSigVerify(const SourceF: TFile; const SourceFS: TFileStream;
@@ -31,13 +33,11 @@ type
 procedure ExtractTemporaryFile(const BaseName: String);
 function ExtractTemporaryFiles(const Pattern: String): Integer;
 function DownloadFile(const Url, CustomUserName, CustomPassword: String;
-  const DestF: TFile; const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString;
-  const ISSigSourceFilename: String;
+  const DestF: TFile; [ref] const Verification: TSetupFileVerification; const ISSigSourceFilename: String;
   const OnSimpleDownloadProgress: TOnSimpleDownloadProgress;
   const OnSimpleDownloadProgressParam: Integer64): Int64;
-function DownloadTemporaryFile(const Url, BaseName, RequiredSHA256OfFile: String;
-  const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString;
-  const OnDownloadProgress: TOnDownloadProgress): Int64;
+function DownloadTemporaryFile(const Url, BaseName: String;
+  [ref] const Verification: TSetupFileVerification; const OnDownloadProgress: TOnDownloadProgress): Int64;
 function DownloadTemporaryFileSize(const Url: String): Int64;
 function DownloadTemporaryFileDate(const Url: String): String;
 procedure SetDownloadTemporaryFileCredentials(const User, Pass: String);
@@ -46,7 +46,7 @@ function GetISSigUrl(const Url, ISSigUrl: String): String;
 implementation
 
 uses
-  Windows, SysUtils, Messages, Forms, ShlObj, Shared.Struct, Setup.UninstallLog,
+  Windows, SysUtils, Messages, Forms, ShlObj, Setup.UninstallLog,
   SetupLdrAndSetup.InstFunc, Setup.InstFunc, Setup.InstFunc.Ole, Setup.SecurityFunc, SetupLdrAndSetup.Messages,
   Setup.MainFunc, Setup.LoggingFunc, Setup.FileExtractor,
   Compression.Base, PathFunc, ISSigFunc, Shared.CommonFunc.Vcl, Compression.SevenZipDLLDecoder,
@@ -287,18 +287,24 @@ begin
   end;
 end;
 
-procedure ISSigVerifyError(const AError: TISSigVerifySignatureError;
+function NoVerification: TSetupFileVerification;
+begin
+  Result := Default(TSetupFileVerification);
+  Result.Typ := fvNone;
+end;
+
+procedure VerificationError(const AError: TVerificationError;
   const ASigFilename: String);
 const
-  LogMessages: array[TISSigVerifySignatureError] of String =
+  LogMessages: array[TVerificationError] of String =
     ('Signature file does not exist', 'Signature is malformed', 'No matching key found',
      'Signature is bad', 'File size is incorrect', 'File hash is incorrect');
-  SetupMessageIDs: array[TISSigVerifySignatureError] of TSetupMessageID =
+  SetupMessageIDs: array[TVerificationError] of TSetupMessageID =
     (msgVerificationSignatureDoesntExist, msgVerificationSignatureInvalid, msgVerificationKeyNotFound,
      msgVerificationSignatureInvalid, msgVerificationFileSizeIncorrect, msgVerificationFileHashIncorrect);
 begin
   { Also see Compiler.SetupCompiler for a similar function }
-  Log('ISSig verification error: ' + AddPeriod(LogMessages[AError]));
+  Log('Verification error: ' + AddPeriod(LogMessages[AError]));
   raise Exception.Create(FmtSetupMessage1(msgSourceVerificationFailed,
     FmtSetupMessage1(SetupMessageIDs[AError], PathExtractName(ASigFilename)))); { Not all messages actually have a %1 parameter but that's OK }
 end;
@@ -319,14 +325,14 @@ begin
     nil,
     procedure(const Filename, SigFilename: String)
     begin
-      ISSigVerifyError(vseSignatureMissing, SigFilename);
+      VerificationError(veSignatureMissing, SigFilename);
     end,
     procedure(const Filename, SigFilename: String; const VerifyResult: TISSigVerifySignatureResult)
     begin
       case VerifyResult of
-        vsrMalformed:  ISSigVerifyError(vseSignatureMalformed, SigFilename);
-        vsrBad: ISSigVerifyError(vseSignatureBad, SigFilename);
-        vsrKeyNotFound: ISSigVerifyError(vseKeyNotFound, SigFilename);
+        vsrMalformed:  VerificationError(veSignatureMalformed, SigFilename);
+        vsrBad: VerificationError(veSignatureBad, SigFilename);
+        vsrKeyNotFound: VerificationError(veKeyNotFound, SigFilename);
       else
         InternalError('Unknown ISSigVerifySignature result');
       end;
@@ -339,15 +345,15 @@ begin
   else
     FileSize := SourceFS.Size;
   if FileSize <> ExpectedFileSize then
-    ISSigVerifyError(vseFileSizeIncorrect);
+    VerificationError(veFileSizeIncorrect);
   { Caller must check ExpectedFileHash }
 end;
 
 const
-  ISSigVerificationSuccessfulLogMessage = 'ISSig verification successful.';
+  VerificationSuccessfulLogMessage = 'Verification successful.';
 
 procedure CopySourceFileToDestFile(const SourceF, DestF: TFile;
-  const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString; const ISSigSourceFilename: String;
+  [ref] const Verification: TSetupFileVerification; const ISSigSourceFilename: String;
   const AExpectedSize: Integer64);
 { Copies all bytes from SourceF to DestF, incrementing process meter as it
   goes. Assumes file pointers of both are 0. }
@@ -358,8 +364,11 @@ var
   Context: TSHA256Context;
 begin
   var ExpectedFileHash: TSHA256Digest;
-  if ISSigVerify then begin
-    DoISSigVerify(SourceF, nil, ISSigSourceFilename, ISSigAllowedKeys, ExpectedFileHash);
+  if Verification.Typ <> fvNone then begin
+    if Verification.Typ = fvHash then
+      ExpectedFileHash := Verification.Hash
+    else
+      DoISSigVerify(SourceF, nil, ISSigSourceFilename, Verification.ISSigAllowedKeys, ExpectedFileHash);
     { ExpectedFileHash checked below after copy }
     SHA256Init(Context);
   end;
@@ -385,16 +394,16 @@ begin
     DestF.WriteBuffer(Buf, BufSize);
     Dec64(BytesLeft, BufSize);
 
-    if ISSigVerify then
+    if Verification.Typ <> fvNone then
       SHA256Update(Context, Buf, BufSize);
 
     ExternalProgressProc64(To64(BufSize), MaxProgress);
   end;
 
-  if ISSigVerify then begin
+  if Verification.Typ <> fvNone then begin
     if not SHA256DigestsEqual(SHA256Final(Context), ExpectedFileHash) then
-      ISSigVerifyError(vseFileHashIncorrect);
-    Log(ISSigVerificationSuccessfulLogMessage);
+      VerificationError(veFileHashIncorrect);
+    Log(VerificationSuccessfulLogMessage);
   end;
 
   { In case the source file was shorter than we thought it was, bump the
@@ -1196,7 +1205,6 @@ var
     CurFileVersionInfoValid: Boolean;
     CurFileVersionInfo, ExistingVersionInfo: TFileVersionNumbers;
     CurFileDateValid, ExistingFileDateValid: Boolean;
-    CurFileHash, ExistingFileHash: TSHA256Digest;
     IsProtectedFile, AllowTimeStampComparison: Boolean;
     DeleteFlags: Longint;
     CurFileDate, ExistingFileDate: TFileTime;
@@ -1386,7 +1394,9 @@ var
                    not(foOverwriteSameVersion in CurFile^.Options) then begin
                   if foReplaceSameVersionIfContentsDiffer in CurFile^.Options then begin
                     { Get the two files' SHA-256 hashes and compare them }
+                    var ExistingFileHash: TSHA256Digest;
                     if TryToGetSHA256OfFile(DisableFsRedir, DestFile, ExistingFileHash) then begin
+                      var CurFileHash: TSHA256Digest;
                       if Assigned(CurFileLocation) then
                         CurFileHash := CurFileLocation^.SHA256Sum
                       else begin
@@ -1585,18 +1595,18 @@ var
               const DownloadPassword = ExpandConst(CurFile^.DownloadPassword);
               var MaxProgress := CurProgress;
               Inc6464(MaxProgress, AExternalSize);
-              if foISSigVerify in CurFile^.Options then begin
+              if CurFile^.Verification.Typ = fvISSig then begin
                 const ISSigTempFile = TempFile + ISSigExt;
                 const ISSigDestF = TFileRedir.Create(DisableFsRedir, ISSigTempFile, fdCreateAlways, faReadWrite, fsNone);
                 try
                   { Download the .issig file }
                   const ISSigUrl = GetISSigUrl(SourceFile, ExpandConst(CurFile^.DownloadISSigSource));
                   DownloadFile(ISSigUrl, DownloadUserName, DownloadPassword,
-                    ISSigDestF, False, '', '', JustProcessEventsProc64, To64(0));
+                    ISSigDestF, NoVerification, '', JustProcessEventsProc64, To64(0));
                   FreeAndNil(ISSigDestF);
                   { Download and verify the actual file }
                   DownloadFile(SourceFile, DownloadUserName, DownloadPassword,
-                    DestF, True, CurFile^.ISSigAllowedKeys, TempFile, ExternalProgressProc64, MaxProgress);
+                    DestF, CurFile^.Verification, TempFile, ExternalProgressProc64, MaxProgress);
                 finally
                   ISSigDestF.Free;
                   { Delete the .issig file }
@@ -1604,7 +1614,7 @@ var
                 end;
               end else
                 DownloadFile(SourceFile, DownloadUserName, DownloadPassword,
-                  DestF, False, '', '', ExternalProgressProc64, MaxProgress);
+                  DestF, CurFile^.Verification, '', ExternalProgressProc64, MaxProgress);
             end
             else begin
               { Copy a duplicated non-external file, or an external file }
@@ -1612,11 +1622,11 @@ var
               try
                 LastOperation := SetupMessages[msgErrorCopying];
                 if Assigned(CurFileLocation) then
-                  CopySourceFileToDestFile(SourceF, DestF, False,
-                    '', '', CurFileLocation^.OriginalSize)
+                  CopySourceFileToDestFile(SourceF, DestF, NoVerification,
+                    '', CurFileLocation^.OriginalSize)
                 else
-                  CopySourceFileToDestFile(SourceF, DestF, foISSigVerify in CurFile^.Options,
-                    CurFile^.ISSigAllowedKeys, SourceFile, AExternalSize);
+                  CopySourceFileToDestFile(SourceF, DestF, CurFile^.Verification,
+                    SourceFile, AExternalSize);
               finally
                 SourceF.Free;
               end;
@@ -2023,25 +2033,29 @@ var
         InternalError('Unexpected custom DestName');
       const DestDir = ExpandConst(CurFile^.DestName);
 
-      var ISSigVerifySourceF: TFile := nil;
+      var VerifySourceF: TFile := nil;
       try
         var FindData: TWin32FindData;
         var H: TArchiveFindHandle := INVALID_HANDLE_VALUE;
         var Failed: String;
         repeat
           try
-            if foISSigVerify in CurFile^.Options then begin
-              if ISSigVerifySourceF = nil then
-                ISSigVerifySourceF := TFileRedir.Create(DisableFsRedir, ArchiveFilename, fdOpenExisting, faRead, fsRead);
+            if CurFile^.Verification.Typ <> fvNone then begin
+              if VerifySourceF = nil then
+                VerifySourceF := TFileRedir.Create(DisableFsRedir, ArchiveFilename, fdOpenExisting, faRead, fsRead);
               var ExpectedFileHash: TSHA256Digest;
-              DoISSigVerify(ISSigVerifySourceF, nil, ArchiveFilename, CurFile^.ISSigAllowedKeys,
+              if CurFile^.Verification.Typ = fvHash then
+                ExpectedFileHash := CurFile^.Verification.Hash
+              else begin
+                DoISSigVerify(VerifySourceF, nil, ArchiveFilename, CurFile^.Verification.ISSigAllowedKeys,
                 ExpectedFileHash);
+              end;
               { Can't get the SHA-256 while extracting so need to get and check it now }
-              const ActualFileHash = GetSHA256OfFile(ISSigVerifySourceF);
+              const ActualFileHash = GetSHA256OfFile(VerifySourceF);
               if not SHA256DigestsEqual(ActualFileHash, ExpectedFileHash) then
-                ISSigVerifyError(vseFileHashIncorrect);
-              Log(ISSigVerificationSuccessfulLogMessage);
-              { Keeping ISSigVerifySourceF open until extraction has completed }
+                VerificationError(veFileHashIncorrect);
+              Log(VerificationSuccessfulLogMessage);
+              { Keep VerifySourceF open until extraction has completed to prevent TOCTOU problem }
             end;
 
             H := ArchiveFindFirstFileRedir(DisableFsRedir, ArchiveFilename, DestDir,
@@ -2094,7 +2108,7 @@ var
           Log('Successfully extracted the archive.');
         end;
       finally
-        ISSigVerifySourceF.Free;
+        VerifySourceF.Free;
       end;
     end;
 
@@ -3799,8 +3813,7 @@ begin
 end;
 
 function DownloadFile(const Url, CustomUserName, CustomPassword: String;
-  const DestF: TFile; const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString;
-  const ISSigSourceFilename: String;
+  const DestF: TFile; [ref] const Verification: TSetupFileVerification; const ISSigSourceFilename: String;
   const OnSimpleDownloadProgress: TOnSimpleDownloadProgress;
   const OnSimpleDownloadProgressParam: Integer64): Int64;
 var
@@ -3855,14 +3868,17 @@ begin
       Result := HandleStream.Size;
       FreeAndNil(HandleStream);
 
-      { Check .issig if specified, otherwise check everything else we can check }
-      if ISSigVerify then begin
+      { Check verification if specified, otherwise check everything else we can check }
+      if Verification.Typ <> fvNone then begin
         var ExpectedFileHash: TSHA256Digest;
-        DoISSigVerify(DestF, nil, ISSigSourceFilename, ISSigAllowedKeys, ExpectedFileHash);
+        if Verification.Typ = fvHash then
+          ExpectedFileHash := Verification.Hash
+        else
+          DoISSigVerify(DestF, nil, ISSigSourceFilename, Verification.ISSigAllowedKeys, ExpectedFileHash);
         const FileHash = GetSHA256OfFile(DestF);
         if not SHA256DigestsEqual(FileHash, ExpectedFileHash) then
-          ISSigVerifyError(vseFileHashIncorrect);
-        Log(ISSigVerificationSuccessfulLogMessage);
+          VerificationError(veFileHashIncorrect);
+        Log(VerificationSuccessfulLogMessage);
       end else begin
         if HTTPDataReceiver.ProgressMax > 0 then begin
           if HTTPDataReceiver.Progress <> HTTPDataReceiver.ProgressMax then
@@ -3879,9 +3895,8 @@ begin
   end;
 end;
 
-function DownloadTemporaryFile(const Url, BaseName, RequiredSHA256OfFile: String;
-  const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString;
-  const OnDownloadProgress: TOnDownloadProgress): Int64;
+function DownloadTemporaryFile(const Url, BaseName: String;
+  [ref] const Verification: TSetupFileVerification; const OnDownloadProgress: TOnDownloadProgress): Int64;
 var
   DestFile, TempFile: String;
   TempF: TFile;
@@ -3890,7 +3905,6 @@ var
   HTTPDataReceiver: THTTPDataReceiver;
   HTTPClient: THTTPClient;
   HTTPResponse: IHTTPResponse;
-  SHA256OfFile: String;
   RetriesLeft: Integer;
   LastError: DWORD;
   User, Pass, CleanUrl: String;
@@ -3909,10 +3923,16 @@ begin
 
   { Prepare directory }
   if NewFileExists(DestFile) then begin
-    if ISSigVerify then begin
+    if Verification.Typ = fvHash then begin
+      if SHA256DigestsEqual(GetSHA256OfFile(False, DestFile), Verification.Hash) then begin
+        Log('  File already downloaded.');
+        Result := 0;
+        Exit;
+      end;
+    end else if Verification.Typ = fvISSig then begin
       var ExistingFileSize: Int64;
       var ExistingFileHash: TSHA256Digest;
-      if ISSigVerifySignature(DestFile, GetISSigAllowedKeys(ISSigAvailableKeys, ISSigAllowedKeys),
+      if ISSigVerifySignature(DestFile, GetISSigAllowedKeys(ISSigAvailableKeys, Verification.ISSigAllowedKeys),
            ExistingFileSize, ExistingFileHash, nil, nil, nil) then begin
         const DestF = TFile.Create(DestFile, fdOpenExisting, faRead, fsReadWrite);
         try
@@ -3926,11 +3946,6 @@ begin
           DestF.Free;
         end;
       end;
-    end else if (RequiredSHA256OfFile <> '') and
-                SameText(RequiredSHA256OfFile, SHA256DigestToString(GetSHA256OfFile(False, DestFile))) then begin
-      Log('  File already downloaded.');
-      Result := 0;
-      Exit;
     end;
 
     SetFileAttributes(PChar(DestFile), GetFileAttributes(PChar(DestFile)) and not FILE_ATTRIBUTE_READONLY);
@@ -3991,26 +4006,21 @@ begin
       Result := HandleStream.Size;
       FreeAndNil(HandleStream);
 
-      { Check .issig or hash if specified, otherwise check everything else we can check }
-      if ISSigVerify then begin
+      { Check verification if specified, otherwise check everything else we can check }
+      if Verification.Typ <> fvNone then begin
         var ExpectedFileHash: TSHA256Digest;
-        DoISSigVerify(TempF, nil, DestFile, ISSigAllowedKeys, ExpectedFileHash);
+        if Verification.Typ = fvHash then
+          ExpectedFileHash := Verification.Hash
+        else
+          DoISSigVerify(TempF, nil, DestFile, Verification.ISSigAllowedKeys, ExpectedFileHash);
         FreeAndNil(TempF);
         const FileHash = GetSHA256OfFile(False, TempFile);
         if not SHA256DigestsEqual(FileHash, ExpectedFileHash) then
-          ISSigVerifyError(vseFileHashIncorrect);
-        Log(ISSigVerificationSuccessfulLogMessage);
+          VerificationError(veFileHashIncorrect);
+        Log(VerificationSuccessfulLogMessage);
       end else begin
         FreeAndNil(TempF);
-        if RequiredSHA256OfFile <> '' then begin
-          try
-            SHA256OfFile := SHA256DigestToString(GetSHA256OfFile(False, TempFile));
-          except on E: Exception do
-            raise Exception.Create(FmtSetupMessage(msgErrorFileHash1, [E.Message]));
-          end;
-          if not SameText(RequiredSHA256OfFile, SHA256OfFile) then
-            raise Exception.Create(FmtSetupMessage(msgErrorFileHash2, [RequiredSHA256OfFile, SHA256OfFile]));
-        end else if HTTPDataReceiver.ProgressMax > 0 then begin
+        if HTTPDataReceiver.ProgressMax > 0 then begin
           if HTTPDataReceiver.Progress <> HTTPDataReceiver.ProgressMax then
             raise Exception.Create(FmtSetupMessage(msgErrorProgress, [IntToStr(HTTPDataReceiver.Progress), IntToStr(HTTPDataReceiver.ProgressMax)]))
           else if HTTPDataReceiver.ProgressMax <> Result then

+ 14 - 11
Projects/Src/Setup.ScriptDlg.pas

@@ -13,7 +13,7 @@ interface
 
 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, StdCtrls, Contnrs, Generics.Collections,
-  Setup.WizardForm, Setup.Install, Compression.SevenZipDecoder,
+  Shared.Struct, Setup.WizardForm, Setup.Install, Compression.SevenZipDecoder,
   NewCheckListBox, NewStaticText, NewProgressBar, PasswordEdit, RichEditViewer,
   BidiCtrls, TaskbarProgressFunc;
 
@@ -173,9 +173,8 @@ type
   end;
 
   TDownloadFile = class
-    Url, BaseName, RequiredSHA256OfFile, UserName, Password: String;
-    ISSigVerify: Boolean;
-    ISSigAllowedKeys: AnsiString;
+    Url, BaseName, UserName, Password: String;
+    Verification: TSetupFileVerification;
   end;
   TDownloadFiles = TObjectList<TDownloadFile>;
 
@@ -249,8 +248,8 @@ function ConvertAllowedKeysRuntimeIDsToISSigAllowedKeys(const AllowedKeysRuntime
 implementation
 
 uses
-  StrUtils, ISSigFunc,
-  Shared.Struct, Shared.SetupTypes, Setup.MainFunc, Setup.SelectFolderForm,
+  StrUtils, ISSigFunc, SHA256,
+  Shared.SetupTypes, Setup.MainFunc, Setup.SelectFolderForm,
   SetupLdrAndSetup.Messages, Shared.SetupMessageIDs, PathFunc, Shared.CommonFunc.Vcl,
   Shared.CommonFunc, BrowseFunc, Setup.LoggingFunc, Setup.InstFunc,
   Compression.SevenZipDLLDecoder;
@@ -1063,11 +1062,16 @@ begin
   var F := TDownloadFile.Create;
   F.Url := Url;
   F.BaseName := BaseName;
-  F.RequiredSHA256OfFile := RequiredSHA256OfFile;
   F.UserName := UserName;
   F.Password := Password;
-  F.ISSigVerify := ISSigVerify;
-  F.ISSigAllowedKeys := ISSigAllowedKeys;
+  F.Verification := NoVerification;
+  if RequiredSHA256OfFile <> '' then begin
+    F.Verification.Typ := fvHash;
+    F.Verification.Hash := SHA256DigestFromString(RequiredSHA256OfFile)
+  end else if ISSigVerify then begin
+    F.Verification.Typ := fvISSig;
+    F.Verification.ISSigAllowedKeys := ISSigAllowedKeys
+  end;
   Result := FFiles.Add(F);
 end;
 
@@ -1132,8 +1136,7 @@ begin
   for var F in FFiles do begin
     { Don't need to set DownloadTemporaryFileOrExtractArchiveProcessMessages before downloading since we already process messages ourselves }
     SetDownloadTemporaryFileCredentials(F.UserName, F.Password);
-    Result := Result + DownloadTemporaryFile(F.Url, F.BaseName, F.RequiredSHA256OfFile,
-      F.ISSigVerify, F.ISSigAllowedKeys, InternalOnDownloadProgress);
+    Result := Result + DownloadTemporaryFile(F.Url, F.BaseName, F.Verification, InternalOnDownloadProgress);
   end;
   SetDownloadTemporaryFileCredentials('', '');
 end;

+ 12 - 3
Projects/Src/Setup.ScriptFunc.pas

@@ -822,10 +822,19 @@ var
         OnDownloadProgress := TOnDownloadProgress(Stack.GetProc(PStart-4, Caller));
       end;
 
+      var Verification := NoVerification;
+      if RequiredSHA256OfFile <> '' then begin
+        Verification.Typ := fvHash;
+        Verification.Hash := SHA256DigestFromString(RequiredSHA256OfFile)
+      end else if ISSigVerify then begin
+        Verification.Typ := fvISSig;
+        Verification.ISSigAllowedKeys := ISSigAllowedKeys
+      end;
+
       { Also see Setup.ScriptDlg TDownloadWizardPage.AddExWithISSigVerify }
       if ISSigVerify then
-        DownloadTemporaryFile(GetISSigUrl(Url, ISSigUrl), BaseName + ISSigExt, '', False, '', OnDownloadProgress);
-      Stack.SetInt64(PStart, DownloadTemporaryFile(Url, BaseName, RequiredSHA256OfFile, ISSigVerify, ISSigAllowedKeys, OnDownloadProgress));
+        DownloadTemporaryFile(GetISSigUrl(Url, ISSigUrl), BaseName + ISSigExt, NoVerification, OnDownloadProgress);
+      Stack.SetInt64(PStart, DownloadTemporaryFile(Url, BaseName, Verification, OnDownloadProgress));
     end);
     RegisterScriptFunc('DownloadTemporaryFileSize', sfNoUninstall, procedure(const Caller: TPSExec; const OrgName: AnsiString; const Stack: TPSStack; const PStart: Cardinal)
     begin
@@ -1864,7 +1873,7 @@ var
          { Couldn't get the SHA-256 while downloading so need to get and check it now }
         const ActualFileHash = ISSigCalcStreamHash(F);
         if not SHA256DigestsEqual(ActualFileHash, ExpectedFileHash) then
-          ISSigVerifyError(vseFileHashIncorrect);
+          VerificationError(veFileHashIncorrect);
       except
         FreeAndNil(F);
         raise;

+ 0 - 2
Projects/Src/Shared.SetupMessageIDs.pas

@@ -93,8 +93,6 @@ type
     msgErrorExtracting,
     msgErrorExtractionAborted,
     msgErrorExtractionFailed,
-    msgErrorFileHash1,
-    msgErrorFileHash2,
     msgErrorFileSize,
     msgErrorFunctionFailed,
     msgErrorFunctionFailedNoCode,

+ 2 - 2
Projects/Src/Shared.SetupTypes.pas

@@ -39,8 +39,8 @@ type
 
   TArrayOfECDSAKey = array of TECDSAKey;
 
-  TISSigVerifySignatureError = (vseSignatureMissing, vseSignatureMalformed, vseKeyNotFound,
-    vseSignatureBad, vseFileSizeIncorrect, vseFileHashIncorrect);
+  TVerificationError = (veSignatureMissing, veSignatureMalformed, veKeyNotFound,
+    veSignatureBad, veFileSizeIncorrect, veFileHashIncorrect);
 
 const
   crHand = 1;

+ 8 - 2
Projects/Src/Shared.Struct.pas

@@ -231,11 +231,17 @@ const
   SetupFileEntryAnsiStrings = 1;
 type
   PSetupFileEntry = ^TSetupFileEntry;
+  TSetupFileVerificationType = (fvNone, fvHash, fvISSig);
+  TSetupFileVerification = packed record
+    ISSigAllowedKeys: AnsiString; { Must be first }
+    Hash: TSHA256Digest;
+    Typ: TSetupFileVerificationType;
+  end;
   TSetupFileEntry = packed record
     SourceFilename, DestName, InstallFontName, StrongAssemblyName, Components,
     Tasks, Languages, Check, AfterInstall, BeforeInstall, Excludes,
     DownloadISSigSource, DownloadUserName, DownloadPassword, ExtractArchivePassword: String;
-    ISSigAllowedKeys: AnsiString;
+    Verification: TSetupFileVerification; { Must be first after strings }
     MinVersion, OnlyBelowVersion: TSetupVersionData;
     LocationEntry: Integer;
     Attribs: Integer;
@@ -251,7 +257,7 @@ type
       foRecurseSubDirsExternal, foReplaceSameVersionIfContentsDiffer,
       foDontVerifyChecksum, foUninsNoSharedFilePrompt, foCreateAllSubDirs,
       fo32Bit, fo64Bit, foExternalSizePreset, foSetNTFSCompression,
-      foUnsetNTFSCompression, foGacInstall, foISSigVerify, foDownload,
+      foUnsetNTFSCompression, foGacInstall, foDownload,
       foExtractArchive);
     FileType: (ftUserFile, ftUninstExe);
   end;

+ 23 - 7
whatsnew.htm

@@ -137,12 +137,16 @@ Name: bosskey; KeyFile: "boss.ispublickey"</pre>
     <li>Note: The <tt>issigverify</tt> flag cannot be combined with the <tt>sign</tt> or <tt>signonce</tt> flags. Use <tt>signcheck</tt> instead.</li>
     <li>Example section:
       <pre>[Files]
-Source: "MyProg.exe"; DestDir: "{app}"; Flags: issigverify; \
-  ISSigAllowedKeys: exesigner bosskey
-Source: "MyProg.chm"; DestDir: "{app}"; Flags: issigverify; \
-  ISSigAllowedKeys: docsigner bosskey
-Source: "{src}\Extra\*.chm"; DestDir: "{app}"; Flags: issigverify external; \
-  ISSigAllowedKeys: docsigner bosskey; Excludes: "*.issig"</pre>
+Source: "MyProg.exe"; DestDir: "{app}"; \
+  ISSigAllowedKeys: "exesigner bosskey"; Flags: issigverify 
+Source: "MyProg.chm"; DestDir: "{app}"; \
+  ISSigAllowedKeys: "docsigner bosskey"; Flags: issigverify
+Source: "{src}\Extra\*.chm"; DestDir: "{app}"; \
+  ISSigAllowedKeys: "docsigner bosskey"; Flags: issigverify external; \
+  Excludes: "*.issig"
+Source: "https://jrsoftware.org/download.php/is.exe?dontcount=1"; DestDir: "{app}"; \
+  ISSigAllowedKeys: "exesigner bosskey"; Flags: issigverify external download ignoreversion; \
+  DestName: "innosetup-latest.exe"; ExternalSize: 7_000_000</pre>
     </li>
   </ul>
   </li>
@@ -169,6 +173,13 @@ issigtool --key-file="MyKey.ispublickey" verify "MyProg.dll"</pre>
   <li>Other related changes:
   <ul>
     <li>The compiler now verifies that precompiled files like <i>SetupLdr.e32</i> and <i>Setup.e32</i> remain unchanged before using them. Can be disabled using new [Setup] section directive <tt>VerifyPrecompiledFiles</tt>. Doing so is <i>not</i> recommended.</li>
+    <li>Added new [Files] section parameter <tt>Hash</tt>. Instructs the compiler or Setup to do a simple SHA-256 hash check instead of a full signature verification, as an alternative to using the <tt>issigverify</tt> flag.<br/>
+        Example script:
+      <pre>[Files]
+Source: "https://jrsoftware.org/download.php/iscrypt.dll?dontcount=1"; DestName: "ISCrypt.dll"; DestDir: "{app}"; \
+  Hash: "2f6294f9aa09f59a574b5dcd33be54e16b39377984f3d5658cda44950fa0f8fc"; \
+  ExternalSize: 2560; Flags: external download ignoreversion</pre>
+    </li>
     <li>Pascal Scripting:
       <ul>
         <li>Added new <tt>ISSigVerify</tt> and <tt>DownloadTemporaryFileWithISSigVerify</tt> support functions.</li>
@@ -205,8 +216,13 @@ issigtool --key-file="MyKey.ispublickey" verify "MyProg.dll"</pre>
   <li>Minor tweaks.</li>
 </ul>
 
-<p>Some messages have been added and changed in this version: (<a href="https://github.com/jrsoftware/issrc/commit/f832ee26">View differences in Default.isl</a>.)</p>
+<p>Some messages have been removed and added in this version: (<a href="https://github.com/jrsoftware/issrc/commit/9224d13e">View differences in Default.isl</a>.)</p>
 <ul>
+  <li><b>Removed messages:</b>
+  <ul>
+    <li>ErrorFileHash1, ErrorFileHash2.</li>
+  </ul>
+  </li>
   <li><b>New messages:</b>
   <ul>
     <li>ArchiveIncorrectPassword, ArchiveIsCorrupted, ArchiveUnsupportedFormat.</li>