瀏覽代碼

ISSigTool: Add support for an issig-v2 format which also checks the base filename, case insensitively.

Verifying doesn't work for Unicode filenames because of the two checks against NonControlASCIICharsSet in ISSigLoadTextFromFile and ISSigVerifySignatureText. Not yet sure what to replace it with. Signing them does work though.

Still supports verification of the issig-v1 format.
Martijn Laan 3 月之前
父節點
當前提交
22c8f729e2
共有 2 個文件被更改,包括 42 次插入24 次删除
  1. 30 20
      Components/ISSigFunc.pas
  2. 12 4
      Projects/ISSigTool.dpr

+ 30 - 20
Components/ISSigFunc.pas

@@ -28,21 +28,22 @@ function ISSigLoadTextFromFile(const AFilename: String): String;
 procedure ISSigSaveTextToFile(const AFilename, AText: String);
 procedure ISSigSaveTextToFile(const AFilename, AText: String);
 
 
 function ISSigCreateSignatureText(const AKey: TECDSAKey;
 function ISSigCreateSignatureText(const AKey: TECDSAKey;
-  const AFileSize: Int64; const AFileHash: TSHA256Digest): String;
+  const AFileName: String; const AFileSize: Int64; const AFileHash: TSHA256Digest): String;
 function ISSigVerifySignatureText(const AAllowedKeys: array of TECDSAKey;
 function ISSigVerifySignatureText(const AAllowedKeys: array of TECDSAKey;
-  const AText: String; out AFileSize: Int64;
+  const AText: String; out AFileName: String; out AFileSize: Int64;
   out AFileHash: TSHA256Digest): TISSigVerifySignatureResult; overload;
   out AFileHash: TSHA256Digest): TISSigVerifySignatureResult; overload;
 function ISSigVerifySignatureText(const AAllowedKeys: array of TECDSAKey;
 function ISSigVerifySignatureText(const AAllowedKeys: array of TECDSAKey;
-  const AText: String; out AFileSize: Int64;
+  const AText: String; out AFileName: String; out AFileSize: Int64;
   out AFileHash: TSHA256Digest; out AKeyUsedID: String): TISSigVerifySignatureResult; overload;
   out AFileHash: TSHA256Digest; out AKeyUsedID: String): TISSigVerifySignatureResult; overload;
 
 
 function ISSigVerifySignature(const AFilename: String; const AAllowedKeys: array of TECDSAKey;
 function ISSigVerifySignature(const AFilename: String; const AAllowedKeys: array of TECDSAKey;
-  out AExpectedFileSize: Int64; out AExpectedFileHash: TSHA256Digest; out AKeyUsedID: String;
+  out AExpectedFileName: String; out AExpectedFileSize: Int64; out AExpectedFileHash: TSHA256Digest;
+  out AKeyUsedID: String;
   const AFileMissingErrorProc: TISSigVerifySignatureFileMissingErrorProc;
   const AFileMissingErrorProc: TISSigVerifySignatureFileMissingErrorProc;
   const ASigFileMissingErrorProc: TISSigVerifySignatureSigFileMissingErrorProc;
   const ASigFileMissingErrorProc: TISSigVerifySignatureSigFileMissingErrorProc;
   const AVerificationFailedErrorProc: TISSigVerifySignatureVerificationFailedErrorProc): Boolean; overload;
   const AVerificationFailedErrorProc: TISSigVerifySignatureVerificationFailedErrorProc): Boolean; overload;
 function ISSigVerifySignature(const AFilename: String; const AAllowedKeys: array of TECDSAKey;
 function ISSigVerifySignature(const AFilename: String; const AAllowedKeys: array of TECDSAKey;
-  out AExpectedFileSize: Int64; out AExpectedFileHash: TSHA256Digest;
+  out AExpectedFileName: String; out AExpectedFileSize: Int64; out AExpectedFileHash: TSHA256Digest;
   const AFileMissingErrorProc: TISSigVerifySignatureFileMissingErrorProc;
   const AFileMissingErrorProc: TISSigVerifySignatureFileMissingErrorProc;
   const ASigFileMissingErrorProc: TISSigVerifySignatureSigFileMissingErrorProc;
   const ASigFileMissingErrorProc: TISSigVerifySignatureSigFileMissingErrorProc;
   const AVerificationFailedErrorProc: TISSigVerifySignatureVerificationFailedErrorProc): Boolean; overload;
   const AVerificationFailedErrorProc: TISSigVerifySignatureVerificationFailedErrorProc): Boolean; overload;
@@ -93,11 +94,13 @@ begin
   TSHA256Digest(Result) := SHA256DigestFromString(S);
   TSHA256Digest(Result) := SHA256DigestFromString(S);
 end;
 end;
 
 
-function CalcHashToSign(const AFileSize: Int64;
+function CalcHashToSign(const AFileName: String; const AFileSize: Int64;
   const AFileHash: TSHA256Digest): TSHA256Digest;
   const AFileHash: TSHA256Digest): TSHA256Digest;
 begin
 begin
   var Context: TSHA256Context;
   var Context: TSHA256Context;
   SHA256Init(Context);
   SHA256Init(Context);
+  if AFileName <> '' then
+    SHA256Update(Context, Pointer(AFileName)^, Length(AFileName)*SizeOf(AFileName[1]));
   SHA256Update(Context, AFileSize, SizeOf(AFileSize));
   SHA256Update(Context, AFileSize, SizeOf(AFileSize));
   SHA256Update(Context, AFileHash, SizeOf(AFileHash));
   SHA256Update(Context, AFileHash, SizeOf(AFileHash));
   Result := SHA256Final(Context);
   Result := SHA256Final(Context);
@@ -165,7 +168,7 @@ begin
 end;
 end;
 
 
 function ISSigCreateSignatureText(const AKey: TECDSAKey;
 function ISSigCreateSignatureText(const AKey: TECDSAKey;
-  const AFileSize: Int64; const AFileHash: TSHA256Digest): String;
+  const AFileName: String; const AFileSize: Int64; const AFileHash: TSHA256Digest): String;
 begin
 begin
   { File size is limited to 16 digits (enough for >9 EB) }
   { File size is limited to 16 digits (enough for >9 EB) }
   if (AFileSize < 0) or (AFileSize > {$IF CompilerVersion >= 36.0} 9_999_999_999_999_999 {$ELSE} 9999999999999999 {$ENDIF} ) then
   if (AFileSize < 0) or (AFileSize > {$IF CompilerVersion >= 36.0} 9_999_999_999_999_999 {$ELSE} 9999999999999999 {$ENDIF} ) then
@@ -174,18 +177,20 @@ begin
   var PublicKey: TECDSAPublicKey;
   var PublicKey: TECDSAPublicKey;
   AKey.ExportPublicKey(PublicKey);
   AKey.ExportPublicKey(PublicKey);
 
 
-  const HashToSign = CalcHashToSign(AFileSize, AFileHash);
+  const HashToSign = CalcHashToSign(AFileName, AFileSize, AFileHash);
   var Sig: TECDSASignature;
   var Sig: TECDSASignature;
   AKey.SignHash(HashToSign, Sig);
   AKey.SignHash(HashToSign, Sig);
 
 
   Result := Format(
   Result := Format(
-    'format issig-v1'#13#10 +
+    'format issig-v2'#13#10 +
+    'file-name %s'#13#10 +
     'file-size %d'#13#10 +
     'file-size %d'#13#10 +
     'file-hash %s'#13#10 +
     'file-hash %s'#13#10 +
     'key-id %s'#13#10 +
     'key-id %s'#13#10 +
     'sig-r %s'#13#10 +
     'sig-r %s'#13#10 +
     'sig-s %s'#13#10,
     'sig-s %s'#13#10,
-    [AFileSize,
+    [AFileName,
+     AFileSize,
      SHA256DigestToString(AFileHash),
      SHA256DigestToString(AFileHash),
      SHA256DigestToString(CalcKeyID(PublicKey)),
      SHA256DigestToString(CalcKeyID(PublicKey)),
      ECDSAInt256ToString(Sig.Sig_r),
      ECDSAInt256ToString(Sig.Sig_r),
@@ -193,15 +198,16 @@ begin
 end;
 end;
 
 
 function ISSigVerifySignatureText(const AAllowedKeys: array of TECDSAKey;
 function ISSigVerifySignatureText(const AAllowedKeys: array of TECDSAKey;
-  const AText: String; out AFileSize: Int64;
+  const AText: String; out AFileName: String; out AFileSize: Int64;
   out AFileHash: TSHA256Digest; out AKeyUsedID: String): TISSigVerifySignatureResult;
   out AFileHash: TSHA256Digest; out AKeyUsedID: String): TISSigVerifySignatureResult;
 var
 var
   TextValues: record
   TextValues: record
-    Format, FileSize, FileHash, KeyID, Sig_r, Sig_s: String;
+    Format, FileName, FileSize, FileHash, KeyID, Sig_r, Sig_s: String;
   end;
   end;
 begin
 begin
   { To be extra safe, clear the "out" parameters just in case the caller isn't
   { To be extra safe, clear the "out" parameters just in case the caller isn't
     properly checking the function result }
     properly checking the function result }
+  AFileName := '';
   AFileSize := -1;
   AFileSize := -1;
   FillChar(AFileHash, SizeOf(AFileHash), 0);
   FillChar(AFileHash, SizeOf(AFileHash), 0);
   AKeyUsedID := '';
   AKeyUsedID := '';
@@ -213,7 +219,8 @@ begin
 
 
   var SS := TStringScanner.Create(AText);
   var SS := TStringScanner.Create(AText);
   if not ConsumeLineValue(SS, 'format', TextValues.Format, 8, 8, NonControlASCIICharsSet) or
   if not ConsumeLineValue(SS, 'format', TextValues.Format, 8, 8, NonControlASCIICharsSet) or
-     (TextValues.Format <> 'issig-v1') or
+     ((TextValues.Format <> 'issig-v1') and ((TextValues.Format <> 'issig-v2'))) or
+     ((TextValues.Format = 'issig-v2') and not ConsumeLineValue(SS, 'file-name', TextValues.FileName, 1, MaxInt, NonControlASCIICharsSet)) or
      not ConsumeLineValue(SS, 'file-size', TextValues.FileSize, 1, 16, DigitsSet) or
      not ConsumeLineValue(SS, 'file-size', TextValues.FileSize, 1, 16, DigitsSet) or
      not ConsumeLineValue(SS, 'file-hash', TextValues.FileHash, 64, 64, HexDigitsSet) or
      not ConsumeLineValue(SS, 'file-hash', TextValues.FileHash, 64, 64, HexDigitsSet) or
      not ConsumeLineValue(SS, 'key-id', TextValues.KeyID, 64, 64, HexDigitsSet) or
      not ConsumeLineValue(SS, 'key-id', TextValues.KeyID, 64, 64, HexDigitsSet) or
@@ -242,13 +249,15 @@ begin
     Exit(vsrKeyNotFound);
     Exit(vsrKeyNotFound);
   AKeyUsedID := TextValues.KeyID;
   AKeyUsedID := TextValues.KeyID;
 
 
+  const UnverifiedFileName = TextValues.FileName;
   const UnverifiedFileSize = StrToInt64(TextValues.FileSize);
   const UnverifiedFileSize = StrToInt64(TextValues.FileSize);
   const UnverifiedFileHash = SHA256DigestFromString(TextValues.FileHash);
   const UnverifiedFileHash = SHA256DigestFromString(TextValues.FileHash);
-  const HashToSign = CalcHashToSign(UnverifiedFileSize, UnverifiedFileHash);
+  const HashToSign = CalcHashToSign(UnverifiedFileName, UnverifiedFileSize, UnverifiedFileHash);
   var Sig: TECDSASignature;
   var Sig: TECDSASignature;
   Sig.Sig_r := ECDSAInt256FromString(TextValues.Sig_r);
   Sig.Sig_r := ECDSAInt256FromString(TextValues.Sig_r);
   Sig.Sig_s := ECDSAInt256FromString(TextValues.Sig_s);
   Sig.Sig_s := ECDSAInt256FromString(TextValues.Sig_s);
   if KeyUsed.VerifySignature(HashToSign, Sig) then begin
   if KeyUsed.VerifySignature(HashToSign, Sig) then begin
+    AFileName := UnverifiedFileName;
     AFileSize := UnverifiedFileSize;
     AFileSize := UnverifiedFileSize;
     AFileHash := UnverifiedFileHash;
     AFileHash := UnverifiedFileHash;
     Result := vsrSuccess;
     Result := vsrSuccess;
@@ -257,15 +266,16 @@ begin
 end;
 end;
 
 
 function ISSigVerifySignatureText(const AAllowedKeys: array of TECDSAKey;
 function ISSigVerifySignatureText(const AAllowedKeys: array of TECDSAKey;
-  const AText: String; out AFileSize: Int64;
+  const AText: String; out AFileName: String; out AFileSize: Int64;
   out AFileHash: TSHA256Digest): TISSigVerifySignatureResult;
   out AFileHash: TSHA256Digest): TISSigVerifySignatureResult;
 begin
 begin
   var KeyUsedID: String;
   var KeyUsedID: String;
-  Result := ISSigVerifySignatureText(AAllowedKeys, AText, AFileSize, AFileHash, KeyUsedID);
+  Result := ISSigVerifySignatureText(AAllowedKeys, AText, AFileName, AFileSize, AFileHash, KeyUsedID);
 end;
 end;
 
 
 function ISSigVerifySignature(const AFilename: String; const AAllowedKeys: array of TECDSAKey;
 function ISSigVerifySignature(const AFilename: String; const AAllowedKeys: array of TECDSAKey;
-  out AExpectedFileSize: Int64; out AExpectedFileHash: TSHA256Digest; out AKeyUsedID: String;
+  out AExpectedFileName: String; out AExpectedFileSize: Int64; out AExpectedFileHash: TSHA256Digest;
+  out AKeyUsedID: String;
   const AFileMissingErrorProc: TISSigVerifySignatureFileMissingErrorProc;
   const AFileMissingErrorProc: TISSigVerifySignatureFileMissingErrorProc;
   const ASigFileMissingErrorProc: TISSigVerifySignatureSigFileMissingErrorProc;
   const ASigFileMissingErrorProc: TISSigVerifySignatureSigFileMissingErrorProc;
   const AVerificationFailedErrorProc: TISSigVerifySignatureVerificationFailedErrorProc): Boolean;
   const AVerificationFailedErrorProc: TISSigVerifySignatureVerificationFailedErrorProc): Boolean;
@@ -294,20 +304,20 @@ begin
   end;
   end;
   const SigText = ISSigLoadTextFromFile(SigFilename);
   const SigText = ISSigLoadTextFromFile(SigFilename);
   const VerifyResult = ISSigVerifySignatureText(AAllowedKeys, SigText,
   const VerifyResult = ISSigVerifySignatureText(AAllowedKeys, SigText,
-    AExpectedFileSize, AExpectedFileHash, AKeyUsedID);
+    AExpectedFileName, AExpectedFileSize, AExpectedFileHash, AKeyUsedID);
   Result := VerifyResult = vsrSuccess;
   Result := VerifyResult = vsrSuccess;
   if not Result and Assigned(AVerificationFailedErrorProc) then
   if not Result and Assigned(AVerificationFailedErrorProc) then
     AVerificationFailedErrorProc(AFilename, SigFilename, VerifyResult);
     AVerificationFailedErrorProc(AFilename, SigFilename, VerifyResult);
 end;
 end;
 
 
 function ISSigVerifySignature(const AFilename: String; const AAllowedKeys: array of TECDSAKey;
 function ISSigVerifySignature(const AFilename: String; const AAllowedKeys: array of TECDSAKey;
-  out AExpectedFileSize: Int64; out AExpectedFileHash: TSHA256Digest;
+  out AExpectedFileName: String; out AExpectedFileSize: Int64; out AExpectedFileHash: TSHA256Digest;
   const AFileMissingErrorProc: TISSigVerifySignatureFileMissingErrorProc;
   const AFileMissingErrorProc: TISSigVerifySignatureFileMissingErrorProc;
   const ASigFileMissingErrorProc: TISSigVerifySignatureSigFileMissingErrorProc;
   const ASigFileMissingErrorProc: TISSigVerifySignatureSigFileMissingErrorProc;
   const AVerificationFailedErrorProc: TISSigVerifySignatureVerificationFailedErrorProc): Boolean;
   const AVerificationFailedErrorProc: TISSigVerifySignatureVerificationFailedErrorProc): Boolean;
 begin
 begin
   var KeyUsedID: String;
   var KeyUsedID: String;
-  Result := ISSigVerifySignature(AFilename, AAllowedKeys, AExpectedFileSize,
+  Result := ISSigVerifySignature(AFilename, AAllowedKeys, AExpectedFileName, AExpectedFileSize,
     AExpectedFileHash, KeyUsedID, AFileMissingErrorProc, ASigFileMissingErrorProc,
     AExpectedFileHash, KeyUsedID, AFileMissingErrorProc, ASigFileMissingErrorProc,
     AVerificationFailedErrorProc);
     AVerificationFailedErrorProc);
 end;
 end;

+ 12 - 4
Projects/ISSigTool.dpr

@@ -159,6 +159,7 @@ procedure SignSingleFile(const AKey: TECDSAKey; const AFilename: String);
 begin
 begin
   PrintFmtUnlessQuiet('%s: ', [AFilename], False);
   PrintFmtUnlessQuiet('%s: ', [AFilename], False);
 
 
+  const FileName = PathExtractName(AFilename);
   var FileSize: Int64;
   var FileSize: Int64;
   var FileHash: TSHA256Digest;
   var FileHash: TSHA256Digest;
   const F = TFile.Create(AFilename, fdOpenExisting, faRead, fsRead);
   const F = TFile.Create(AFilename, fdOpenExisting, faRead, fsRead);
@@ -175,18 +176,19 @@ begin
     being re-signed but its contents haven't changed, we attempt to load and
     being re-signed but its contents haven't changed, we attempt to load and
     verify the existing .issig file. If the key, file size, and file hash are
     verify the existing .issig file. If the key, file size, and file hash are
     all up to date, then we skip creation of a new .issig file. }
     all up to date, then we skip creation of a new .issig file. }
+  var ExistingFileName: String;
   var ExistingFileSize: Int64;
   var ExistingFileSize: Int64;
   var ExistingFileHash: TSHA256Digest;
   var ExistingFileHash: TSHA256Digest;
   const Verified = ISSigVerifySignature(AFilename, [AKey],
   const Verified = ISSigVerifySignature(AFilename, [AKey],
-    ExistingFileSize, ExistingFileHash, nil, nil, nil);
+    ExistingFileName, ExistingFileSize, ExistingFileHash, nil, nil, nil);
 
 
-  if Verified and (FileSize = ExistingFileSize) and
+  if Verified and SameText(FileName, ExistingFileName) and (FileSize = ExistingFileSize) and
      SHA256DigestsEqual(FileHash, ExistingFileHash) then begin
      SHA256DigestsEqual(FileHash, ExistingFileHash) then begin
     PrintUnlessQuiet('signature unchanged');
     PrintUnlessQuiet('signature unchanged');
     Exit;
     Exit;
   end;
   end;
 
 
-  const SigText = ISSigCreateSignatureText(AKey, FileSize, FileHash);
+  const SigText = ISSigCreateSignatureText(AKey, FileName, FileSize, FileHash);
   ISSigSaveTextToFile(AFilename + ISSigExt, SigText);
   ISSigSaveTextToFile(AFilename + ISSigExt, SigText);
   PrintUnlessQuiet('signature written');
   PrintUnlessQuiet('signature written');
 end;
 end;
@@ -209,9 +211,10 @@ begin
   Result := False;
   Result := False;
   PrintFmtUnlessQuiet('%s: ', [AFilename], False);
   PrintFmtUnlessQuiet('%s: ', [AFilename], False);
 
 
+  var ExpectedFileName: String;
   var ExpectedFileSize: Int64;
   var ExpectedFileSize: Int64;
   var ExpectedFileHash: TSHA256Digest;
   var ExpectedFileHash: TSHA256Digest;
-  if not ISSigVerifySignature(AFilename, [AKey], ExpectedFileSize, ExpectedFileHash,
+  if not ISSigVerifySignature(AFilename, [AKey], ExpectedFileName, ExpectedFileSize, ExpectedFileHash,
     procedure(const Filename: String)
     procedure(const Filename: String)
     begin
     begin
       PrintUnlessQuiet('MISSINGFILE (File does not exist)');
       PrintUnlessQuiet('MISSINGFILE (File does not exist)');
@@ -234,6 +237,11 @@ begin
   ) then
   ) then
     Exit;
     Exit;
 
 
+  if (ExpectedFileName <> '') and not SameText(PathExtractName(AFilename), ExpectedFileName) then begin
+    PrintUnlessQuiet('WRONGNAME (File name is incorrect)');
+    Exit;
+  end;
+
   const F = TFile.Create(AFilename, fdOpenExisting, faRead, fsRead);
   const F = TFile.Create(AFilename, fdOpenExisting, faRead, fsRead);
   try
   try
     if Int64(F.Size) <> ExpectedFileSize then begin
     if Int64(F.Size) <> ExpectedFileSize then begin