From 813d024dd40a54b84671f63a70d6f7ec595695f0 Mon Sep 17 00:00:00 2001 From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com> Date: Sun, 8 Jun 2025 20:27:45 +0200 Subject: [PATCH] Add download+issigverify support. This finishes the branch, only whatsnew todo + making a new fresh messages commit + test username/password support. --- Components/ISSigFunc.pas | 1 + ISHelp/isetup.xml | 6 +++++ ISHelp/isxfunc.xml | 2 +- Projects/Src/Compiler.SetupCompiler.pas | 7 +++++- Projects/Src/Setup.Install.pas | 32 ++++++++++++++++++++----- Projects/Src/Setup.MainFunc.pas | 2 ++ Projects/Src/Shared.Struct.pas | 4 ++-- 7 files changed, 44 insertions(+), 10 deletions(-) diff --git a/Components/ISSigFunc.pas b/Components/ISSigFunc.pas index e80c1ae5..04b2acb2 100644 --- a/Components/ISSigFunc.pas +++ b/Components/ISSigFunc.pas @@ -70,6 +70,7 @@ function ISSigCalcStreamHash(const AStream: TStream): TSHA256Digest; var ISSigExt: String = '.issig'; + ISSigEstimatedSize: Integer = 330; implementation diff --git a/ISHelp/isetup.xml b/ISHelp/isetup.xml index 925cabb8..f79a8f15 100644 --- a/ISHelp/isetup.xml +++ b/ISHelp/isetup.xml @@ -1668,6 +1668,12 @@ ExternalSize: 1048576; Flags: external

This parameter is ignored if the extractarchive flag isn't also specified.

+ +

Specifies the URL of the .issig signature file which should be downloaded. This file is used to verify the file downloaded from the URL specified by the Source parameter.

+

This parameter is ignored if the download and issigverify flags aren't both also specified.

+

If this parameter is not set but both these flags are used, Setup will instead append ".issig" (without quotes) to the path portion of the URL specified by the Source parameter. It will then use the result as the URL to download the .issig signature file from.

+ +

Specifies the basic authentication username to use for the file download.

This parameter is ignored if the download flag isn't also specified.

diff --git a/ISHelp/isxfunc.xml b/ISHelp/isxfunc.xml index eed631d7..0504edf1 100644 --- a/ISHelp/isxfunc.xml +++ b/ISHelp/isxfunc.xml @@ -1862,7 +1862,7 @@ end; DownloadTemporaryFileWithISSigVerify function DownloadTemporaryFileWithISSigVerify(const Url, ISSigUrl, BaseName: String; const AllowedKeysRuntimeIDs: TStringList; const OnDownloadProgress: TOnDownloadProgress): Int64;

Like DownloadTemporaryFile, but downloads an .issig signature file first from the specified second URL and uses it to verify the main file downloaded from the first URL.

-

If the second URL is an empty string, Setup will instead append ".issig" (without quotes) to the path portion of the first URL and use the result as the URL to download the .issig signature file from.

+

If the second URL is an empty string, Setup will instead append ".issig" (without quotes) to the path portion of the first URL. It will then use the result as the URL to download the .issig signature file from.

Verification uses the specified allowed keys, looked up using [ISSigKeys] section parameter RuntimeID. To allow all keys set AllowedKeysRuntimeIDs to nil.

An exception will be raised if there was an error. Otherwise, returns the number of bytes downloaded for the main file from the first URL. Returns 0 if the main file was already downloaded and still verified.

DownloadTemporaryFile
diff --git a/Projects/Src/Compiler.SetupCompiler.pas b/Projects/Src/Compiler.SetupCompiler.pas index 536d0d14..4f2e76b1 100644 --- a/Projects/Src/Compiler.SetupCompiler.pas +++ b/Projects/Src/Compiler.SetupCompiler.pas @@ -4670,7 +4670,7 @@ type paPermissions, paFontInstall, paExcludes, paExternalSize, paExtractArchivePassword, paStrongAssemblyName, paISSigAllowedKeys, paComponents, paTasks, paLanguages, paCheck, paBeforeInstall, paAfterInstall, paMinVersion, paOnlyBelowVersion, - paDownloadUserName, paDownloadPassword); + paDownloadISSigSource, paDownloadUserName, paDownloadPassword); const ParamFilesSource = 'Source'; ParamFilesDestDir = 'DestDir'; @@ -4684,6 +4684,7 @@ const ParamFilesExtractArchivePassword = 'ExtractArchivePassword'; ParamFilesStrongAssemblyName = 'StrongAssemblyName'; ParamFilesISSigAllowedKeys = 'ISSigAllowedKeys'; + ParamFilesDownloadISSigSource = 'DownloadISSigSource'; ParamFilesDownloadUserName = 'DownloadUserName'; ParamFilesDownloadPassword = 'DownloadPassword'; ParamInfo: array[TParam] of TParamInfo = ( @@ -4700,6 +4701,7 @@ const (Name: ParamFilesExtractArchivePassword; Flags: []), (Name: ParamFilesStrongAssemblyName; Flags: [piNoEmpty]), (Name: ParamFilesISSigAllowedKeys; Flags: [piNoEmpty]), + (Name: ParamFilesDownloadISSigSource; Flags: []), (Name: ParamFilesDownloadUserName; Flags: [piNoEmpty]), (Name: ParamFilesDownloadPassword; Flags: [piNoEmpty]), (Name: ParamCommonComponents; Flags: []), @@ -5350,6 +5352,9 @@ begin Include(Options, foExternalSizePreset); end; + { DownloadISSigSource } + DownloadISSigSource := Values[paDownloadISSigSource].Data; + { DownloadUserName } DownloadUserName := Values[paDownloadUserName].Data; diff --git a/Projects/Src/Setup.Install.pas b/Projects/Src/Setup.Install.pas index 6a05bbb6..b8550959 100644 --- a/Projects/Src/Setup.Install.pas +++ b/Projects/Src/Setup.Install.pas @@ -32,6 +32,7 @@ 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 OnSimpleDownloadProgress: TOnSimpleDownloadProgress): Int64; function DownloadTemporaryFile(const Url, BaseName, RequiredSHA256OfFile: String; const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString; @@ -1561,16 +1562,35 @@ var not (foDontVerifyChecksum in CurFile^.Options)); end else if foExtractArchive in CurFile^.Options then begin - { Extract a file from archive. Note: foISSigVerify for archive has + { Extract a file from archive. Note: ISSigVerify for archive has already been handled by RecurseExternalArchiveCopyFiles. } LastOperation := SetupMessages[msgErrorExtracting]; ArchiveFindExtract(StrToInt(SourceFile), DestF, ExtractorProgressProc); end else if foDownload in CurFile^.Options then begin - { Download a file } + { Download a file with or without ISSigVerify. Note: estimate of + extra .issig size has already been added to CurFile's ExternalSize. } LastOperation := SetupMessages[msgErrorDownloading]; - DownloadFile(SourceFile, CurFile^.DownloadUserName, CurFile^.DownloadPassword, - DestF, foISSigVerify in CurFile^.Options, CurFile^.ISSigAllowedKeys, ExtractorProgressProc); + if foISSigVerify in CurFile^.Options then begin + const ISSigTempFile = TempFile + ISSigExt; + const ISSigDestF = TFileRedir.Create(DisableFsRedir, ISSigTempFile, fdCreateAlways, faReadWrite, fsNone); + try + { Download the .issig file } + const ISSigUrl = GetISSigUrl(SourceFile, CurFile^.DownloadISSigSource); + DownloadFile(ISSigUrl, CurFile^.DownloadUserName, CurFile^.DownloadPassword, + ISSigDestF, False, '', '', ExtractorProgressProc); + FreeAndNil(ISSigDestF); + { Download and verify the actual file } + DownloadFile(SourceFile, CurFile^.DownloadUserName, CurFile^.DownloadPassword, + DestF, True, CurFile^.ISSigAllowedKeys, TempFile, ExtractorProgressProc); + finally + ISSigDestF.Free; + { Delete the .issig file } + DeleteFileRedir(DisableFsRedir, ISSigTempFile); + end; + end else + DownloadFile(SourceFile, CurFile^.DownloadUserName, CurFile^.DownloadPassword, + DestF, False, '', '', ExtractorProgressProc); end else begin { Copy a duplicated non-external file, or an external file } @@ -3768,9 +3788,9 @@ end; function DownloadFile(const Url, CustomUserName, CustomPassword: String; const DestF: TFile; const ISSigVerify: Boolean; const ISSigAllowedKeys: AnsiString; + const ISSigSourceFilename: String; const OnSimpleDownloadProgress: TOnSimpleDownloadProgress): Int64; var - DestFile: String; HandleStream: THandleStream; HTTPDataReceiver: THTTPDataReceiver; HTTPClient: THTTPClient; @@ -3824,7 +3844,7 @@ begin { Check .issig if specified, otherwise check everything else we can check } if ISSigVerify then begin var ExpectedFileHash: TSHA256Digest; - DoISSigVerify(DestF, nil, DestFile, ISSigAllowedKeys, ExpectedFileHash); + DoISSigVerify(DestF, nil, ISSigSourceFilename, ISSigAllowedKeys, ExpectedFileHash); const FileHash = GetSHA256OfFile(DestF); if not SHA256DigestsEqual(FileHash, ExpectedFileHash) then ISSigVerifyError(vseFileHashIncorrect, SetupMessages[msgSourceIsCorrupted]); diff --git a/Projects/Src/Setup.MainFunc.pas b/Projects/Src/Setup.MainFunc.pas index 4a369809..ea7a0122 100644 --- a/Projects/Src/Setup.MainFunc.pas +++ b/Projects/Src/Setup.MainFunc.pas @@ -3550,6 +3550,8 @@ begin (ESevenZipError). Also see EnumFiles. } end; end; + if (foDownload in Options) and (foISSigVerify in Options) then + Inc6464(ExternalSize, Integer64(UInt64(ISSigEstimatedSize))); if Components = '' then { no types or a file that doesn't belong to any component } if (Tasks = '') and (Check = '') then {don't count tasks or scripted entries} Inc6464(MinimumSpace, ExternalSize); diff --git a/Projects/Src/Shared.Struct.pas b/Projects/Src/Shared.Struct.pas index 84605683..4e2e17de 100644 --- a/Projects/Src/Shared.Struct.pas +++ b/Projects/Src/Shared.Struct.pas @@ -227,14 +227,14 @@ type PublicX, PublicY, RuntimeID: String; end; const - SetupFileEntryStrings = 14; + SetupFileEntryStrings = 15; SetupFileEntryAnsiStrings = 1; type PSetupFileEntry = ^TSetupFileEntry; TSetupFileEntry = packed record SourceFilename, DestName, InstallFontName, StrongAssemblyName, Components, Tasks, Languages, Check, AfterInstall, BeforeInstall, Excludes, - DownloadUserName, DownloadPassword, ExtractArchivePassword: String; + DownloadISSigSource, DownloadUserName, DownloadPassword, ExtractArchivePassword: String; ISSigAllowedKeys: AnsiString; MinVersion, OnlyBelowVersion: TSetupVersionData; LocationEntry: Integer;