ソースを参照

Add support functions ISSigLoadTextFromFile and ISSigVerify. I image them being useful if you want to verify a downloaded archive which does not contain .issig files and which you cannot repack. This specific case would require the download to be to a protected folder though for full protection.

Adds support function GetSHA256OfStream as a side-effect.

Todo: doc all three.
Martijn Laan 4 ヶ月 前
コミット
55232b5f9e

+ 20 - 2
Projects/Src/Setup.InstFunc.pas

@@ -12,7 +12,7 @@ unit Setup.InstFunc;
 interface
 
 uses
-  Windows, SysUtils, Shared.Int64Em, SHA256, Shared.CommonFunc;
+  Windows, SysUtils, Classes, Shared.Int64Em, SHA256, Shared.CommonFunc;
 
 type
   PSimpleStringListArray = ^TSimpleStringListArray;
@@ -59,6 +59,7 @@ function GetComputerNameString: String;
 function GetFileDateTime(const DisableFsRedir: Boolean; const Filename: String;
   var DateTime: TFileTime): Boolean;
 function GetSHA256OfFile(const DisableFsRedir: Boolean; const Filename: String): TSHA256Digest;
+function GetSHA256OfStream(const Stream: TStream): TSHA256Digest;
 function GetSHA256OfAnsiString(const S: AnsiString): TSHA256Digest;
 function GetSHA256OfUnicodeString(const S: UnicodeString): TSHA256Digest;
 function GetRegRootKeyName(const RootKey: HKEY): String;
@@ -102,7 +103,7 @@ implementation
 uses
   Messages, ShellApi, PathFunc, SetupLdrAndSetup.InstFunc, SetupLdrAndSetup.Messages,
   Shared.SetupMessageIDs, Shared.FileClass, SetupLdrAndSetup.RedirFunc, Shared.SetupTypes,
-  Classes, RegStr, Math;
+  RegStr, Math;
 
 procedure InternalError(const Id: String);
 begin
@@ -574,6 +575,23 @@ begin
   Result := SHA256Final(Context);
 end;
 
+function GetSHA256OfStream(const Stream: TStream): TSHA256Digest;
+{ Gets SHA-256 sum as a string of the stream. An exception will be raised upon
+  failure. }
+var
+  Buf: array[0..65535] of Byte;
+begin
+  var Context: TSHA256Context;
+  SHA256Init(Context);
+  while True do begin
+    var NumRead := Stream.Read(Buf, SizeOf(Buf));
+    if NumRead = 0 then
+      Break;
+    SHA256Update(Context, Buf, NumRead);
+  end;
+  Result := SHA256Final(Context);
+end;
+
 function GetSHA256OfAnsiString(const S: AnsiString): TSHA256Digest;
 begin
   Result := SHA256Buf(Pointer(S)^, Length(S)*SizeOf(S[1]));

+ 77 - 1
Projects/Src/Setup.ScriptFunc.pas

@@ -21,7 +21,7 @@ implementation
 uses
   Windows,
   Forms, SysUtils, Classes, Graphics, ActiveX, Generics.Collections,
-  uPSUtils, PathFunc, BrowseFunc, MD5, SHA1, SHA256, BitmapImage, PSStackHelper,
+  uPSUtils, PathFunc, ISSigFunc, ECDSA, BrowseFunc, MD5, SHA1, SHA256, BitmapImage, PSStackHelper,
   Shared.Struct, Setup.ScriptDlg, Setup.MainFunc, Shared.CommonFunc.Vcl,
   Shared.CommonFunc, Shared.FileClass, SetupLdrAndSetup.RedirFunc,
   Setup.Install, SetupLdrAndSetup.InstFunc, Setup.InstFunc, Setup.InstFunc.Ole,
@@ -880,6 +880,10 @@ var
     begin
       Stack.SetString(PStart, SHA256DigestToString(GetSHA256OfFile(ScriptFuncDisableFsRedir, Stack.GetString(PStart-1))));
     end);
+    RegisterScriptFunc('GETSHA256OFSTREAM', procedure(const Caller: TPSExec; const OrgName: AnsiString; const Stack: TPSStack; const PStart: Cardinal)
+    begin
+      Stack.SetString(PStart, SHA256DigestToString(GetSHA256OfStream(TStream(Stack.GetClass(PStart-1)))));
+    end);
     RegisterScriptFunc('GETSHA256OFSTRING', procedure(const Caller: TPSExec; const OrgName: AnsiString; const Stack: TPSStack; const PStart: Cardinal)
     begin
       Stack.SetString(PStart, SHA256DigestToString(GetSHA256OfAnsiString(Stack.GetAnsiString(PStart-1))));
@@ -1804,6 +1808,78 @@ var
         Parts := Stack.GetString(PStart-1).Split(Separators, TStringSplitOptions(Stack.GetInt(PStart-3)));
       Stack.SetArray(PStart, Parts);
     end);
+    RegisterScriptFunc('ISSigLoadTextFromFile', procedure(const Caller: TPSExec; const OrgName: AnsiString; const Stack: TPSStack; const PStart: Cardinal)
+    begin
+      Stack.SetString(PStart, ISSigLoadTextFromFile(Stack.GetString(PStart-1)));
+    end);
+    RegisterScriptFunc('ISSigVerify', procedure(const Caller: TPSExec; const OrgName: AnsiString; const Stack: TPSStack; const PStart: Cardinal)
+    begin
+      const AllowedKeysTexts = Stack.GetStringArray(PStart-1);
+      const Filename = Stack.GetString(PStart-2);
+      const KeepOpen = Stack.GetBool(PStart-3);
+
+      { Following is same as ISSigTool's VerifySingleFile but uses TFileStream
+        instead of TFile because we want to return one and scripts don't known TFile }
+
+      if not NewFileExists(Filename) then
+        raise Exception.Create('File does not exist');
+
+      const SigFilename = Filename + '.issig';
+      if not NewFileExists(SigFilename) then
+        raise Exception.Create('Signature file does not exist');
+
+      var AllowedKeys: TArrayOfECDSAKey;
+      const NAllowedKeys = Length(AllowedKeysTexts);
+      SetLength(AllowedKeys, NAllowedKeys);
+      for var I := 0 to NAllowedKeys-1 do
+        AllowedKeys[I] := nil;
+      var F: TFileStream;
+      try
+        { Import keys }
+        for var I := 0 to NAllowedKeys-1 do begin
+          AllowedKeys[I] := TECDSAKey.Create;
+          const ImportResult = ISSigImportKeyText(AllowedKeys[I], AllowedKeysTexts[I], False);
+          if ImportResult = ikrMalformed then
+            InternalError('Key text is malformed')
+          else if ImportResult <> ikrSuccess then
+            InternalError('Unknown import key result');
+        end;
+
+        { Verify signature }
+        const SigText = ISSigLoadTextFromFile(SigFilename);
+        var ExpectedFileSize: Int64;
+        var ExpectedFileHash: TSHA256Digest;
+        const VerifyResult = ISSigVerifySignatureText(AllowedKeys, SigText,
+          ExpectedFileSize, ExpectedFileHash);
+        if VerifyResult <> vsrSuccess then begin
+          case VerifyResult of
+            vsrMalformed, vsrBadSignature:
+              raise Exception.Create('Signature file is not valid');
+            vsrKeyNotFound:
+              raise Exception.Create('Incorrect key ID');
+          else
+            InternalError('Unknown verify result');
+          end;
+        end;
+
+        { Verify file, keeping open afterwards if requested }
+        F := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
+        try
+          if Int64(F.Size) <> ExpectedFileSize then
+            raise Exception.Create('File size is incorrect');
+          const ActualFileHash = GetSHA256OfStream(F);
+          if not SHA256DigestsEqual(ActualFileHash, ExpectedFileHash) then
+            raise Exception.Create('File hash is incorrect');
+        finally
+          if not KeepOpen then
+            FreeAndNil(F);
+        end;
+      finally
+        for var I := 0 to NAllowedKeys-1 do
+          AllowedKeys[I].Free;
+      end;
+      Stack.SetClass(PStart, F);
+    end);
   end;
 
   procedure RegisterDelphiFunction(ProcPtr: Pointer; const Name: AnsiString);

+ 4 - 1
Projects/Src/Shared.ScriptFunc.pas

@@ -338,6 +338,7 @@ initialization
     'function GetSHA1OfString(const S: AnsiString): String;',
     'function GetSHA1OfUnicodeString(const S: String): String;',
     'function GetSHA256OfFile(const Filename: String): String;',
+    'function GetSHA256OfStream(const Stream: TStream): String;',
     'function GetSHA256OfString(const S: AnsiString): String;',
     'function GetSHA256OfUnicodeString(const S: String): String;',
     'function GetSpaceOnDisk(const DriveRoot: String; const InMegabytes: Boolean; var Free, Total: Cardinal): Boolean;',
@@ -542,7 +543,9 @@ initialization
     'function Debugging: Boolean;',
     'function StringJoin(const Separator: String; const Values: TArrayOfString): String;',
     'function StringSplit(const S: String; const Separators: TArrayOfString; const Typ: TSplitType): TArrayOfString;',
-    'function StringSplitEx(const S: String; const Separators: TArrayOfString; const Quote: Char; const Typ: TSplitType): TArrayOfString;'
+    'function StringSplitEx(const S: String; const Separators: TArrayOfString; const Quote: Char; const Typ: TSplitType): TArrayOfString;',
+    'function ISSigLoadTextFromFile(const AFilename: String): String;',
+    'function ISSigVerify(const AllowedKeysTexts: TArrayOfString; const Filename: String; const KeepOpen: Boolean): TFileStream;'
   ];
 
   {$IFDEF COMPIL32PROJ}