Browse Source

Protection on HasValidSignature

Protection to check that used same public key in HasValidSignature
PascalCoin 6 years ago
parent
commit
7c9fad0c48
4 changed files with 78 additions and 13 deletions
  1. 23 0
      src/core/UBlockChain.pas
  2. 0 2
      src/core/UNode.pas
  3. 7 0
      src/core/UOpTransaction.pas
  4. 48 11
      src/core/UTxMultiOperation.pas

+ 23 - 0
src/core/UBlockChain.pas

@@ -204,6 +204,7 @@ Type
     FPrevious_Destination_updated_block : Cardinal;
     FPrevious_Seller_updated_block : Cardinal;
     FHasValidSignature : Boolean;
+    FUsedPubkeyForSignature : TECDSA_Public;
     FBufferedSha256 : TRawBytes;
     procedure InitializeData; virtual;
     function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean; virtual; abstract;
@@ -213,6 +214,7 @@ Type
     Property Previous_Destination_updated_block : Cardinal read FPrevious_Destination_updated_block; // deprecated
     Property Previous_Seller_updated_block : Cardinal read FPrevious_Seller_updated_block; // deprecated
     function IsValidECDSASignature(const PubKey: TECDSA_Public; current_protocol : Word; const Signature: TECDSA_SIG): Boolean;
+    procedure CopyUsedPubkeySignatureFrom(SourceOperation : TPCOperation); virtual;
   public
     constructor Create; virtual;
     destructor Destroy; override;
@@ -2107,6 +2109,8 @@ begin
     P^.Op.FBufferedSha256:=op.FBufferedSha256;
     P^.Op.tag := list.Count;
     P^.Op.FHasValidSignature := op.FHasValidSignature; // Improvement speed v4.0.2 reusing previously signed value
+    P^.Op.FUsedPubkeyForSignature := op.FUsedPubkeyForSignature;
+    P^.Op.CopyUsedPubkeySignatureFrom(op);
     // Improvement TOperationsHashTree speed 2.1.6
     // Include to hash tree (Only if CalcNewHashTree=True)
     If (CalcNewHashTree) And (Length(FHashTree)=32) then begin
@@ -2343,6 +2347,8 @@ begin
               if (opToMark.FHasValidSignature) then inc(nAlreadyMarked)
               else begin
                 opToMark.FHasValidSignature:=True;
+                opToMark.FUsedPubkeyForSignature:=opInMyList.FUsedPubkeyForSignature;
+                opToMark.CopyUsedPubkeySignatureFrom(opInMyList);
                 inc(nMarkedAsGood);
               end;
             end;
@@ -2534,6 +2540,7 @@ constructor TPCOperation.Create;
 begin
   FHasValidSignature := False;
   FBufferedSha256:='';
+  FUsedPubkeyForSignature := CT_TECDSA_Public_Nul;
   InitializeData;
 end;
 
@@ -2713,6 +2720,7 @@ begin
   FPrevious_Destination_updated_block := 0;
   FPrevious_Seller_updated_block := 0;
   FHasValidSignature := false;
+  FUsedPubkeyForSignature:=CT_TECDSA_Public_Nul;
   FBufferedSha256:='';
 end;
 
@@ -2725,12 +2733,27 @@ function TPCOperation.IsValidECDSASignature(const PubKey: TECDSA_Public; current
 begin
   // Will reuse FHasValidSignature if checked previously and was True
   // Introduced on Build 4.0.2 to increase speed using MEMPOOL verified operations instead of verify again everytime
+  if (FHasValidSignature) then begin
+    If Not TAccountComp.EqualAccountKeys(PubKey,FUsedPubkeyForSignature) then begin
+      TLog.NewLog(lterror,ClassName,Format('Detected incorrect previous use of signature used pubkey:%s current pubkey:%s',[TAccountComp.AccountPublicKeyExport(FUsedPubkeyForSignature),TAccountComp.AccountPublicKeyExport(PubKey)]));
+      FHasValidSignature := False;
+      FUsedPubkeyForSignature := CT_TECDSA_Public_Nul;
+    end;
+  end;
   if (Not FHasValidSignature) then begin
     FHasValidSignature := TCrypto.ECDSAVerify(PubKey,GetDigestToSign(current_protocol),Signature);
+    If FHasValidSignature then begin;
+      FUsedPubkeyForSignature := PubKey;
+    end;
   end;
   Result := FHasValidSignature;
 end;
 
+procedure TPCOperation.CopyUsedPubkeySignatureFrom(SourceOperation: TPCOperation);
+begin
+  //
+end;
+
 function TPCOperation.LoadFromNettransfer(Stream: TStream): Boolean;
 begin
   Result := LoadOpFromStream(Stream, False);

+ 0 - 2
src/core/UNode.pas

@@ -456,10 +456,8 @@ begin
               inc(nError);
               if (errors<>'') then errors := errors+' ';
               errors := errors+'Op '+IntToStr(j+1)+'/'+IntToStr(OperationsHashTree.OperationsCount)+':'+e;
-              {$IFDEF HIGHLOG}
               TLog.NewLog(ltdebug,Classname,Format('AddOperation invalid/duplicated %d/%d: %s  - Error:%s',
                 [(j+1),OperationsHashTree.OperationsCount,ActOp.ToString,e]));
-              {$ENDIF}
               if Assigned(OperationsResult) then begin
                 TPCOperation.OperationToOperationResume(0,ActOp,True,ActOp.SignerAccount,OPR);
                 OPR.valid := false;

+ 7 - 0
src/core/UOpTransaction.pas

@@ -640,6 +640,7 @@ begin
   if Assigned(key) then begin
     FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
     FHasValidSignature := true;
+    FUsedPubkeyForSignature := key.PublicKey;
   end else begin
     TLog.NewLog(ltdebug,Classname,'No key for signing a new Change Info operation');
     FHasValidSignature := false;
@@ -726,6 +727,7 @@ begin
   if Assigned(key) then begin
     FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
     FHasValidSignature := true;
+    FUsedPubkeyForSignature := key.PublicKey;
   end else begin
     TLog.NewLog(ltdebug,Classname,'No key for signing a new Transaction');
     FHasValidSignature := false;
@@ -1183,6 +1185,7 @@ begin
   if Assigned(key) then begin
     FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
     FHasValidSignature := true;
+    FUsedPubkeyForSignature := key.PublicKey;
   end else begin
     TLog.NewLog(ltdebug,Classname,'No key for signing a new Change key');
     FHasValidSignature := false;
@@ -2021,6 +2024,7 @@ begin
   if Assigned(key) then begin
     FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
     FHasValidSignature := true;
+    FUsedPubkeyForSignature := key.PublicKey;
   end else begin
     TLog.NewLog(ltdebug,Classname,'No key for signing a new list account for sale operation');
     FHasValidSignature := false;
@@ -2051,6 +2055,7 @@ begin
   if Assigned(key) then begin
     FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
     FHasValidSignature := true;
+    FUsedPubkeyForSignature := key.PublicKey;
   end else begin
     TLog.NewLog(ltdebug,Classname,'No key for signing a delist account operation');
     FHasValidSignature := false;
@@ -2090,6 +2095,7 @@ begin
   if Assigned(key) then begin
     FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
     FHasValidSignature := true;
+    FUsedPubkeyForSignature := key.PublicKey;
   end else begin
     TLog.NewLog(ltdebug,Classname,'No key for signing a new Buy operation');
     FHasValidSignature := false;
@@ -2380,6 +2386,7 @@ begin
   if Assigned(signer_key) then begin
     FData.sign := TCrypto.ECDSASign(signer_key.PrivateKey, GetDigestToSign(CT_PROTOCOL_4));
     FHasValidSignature := true;
+    FUsedPubkeyForSignature := signer_key.PublicKey;
   end else begin
     TLog.NewLog(ltdebug,Classname,'No key for signing a new OpData');
     FHasValidSignature := false;

+ 48 - 11
src/core/UTxMultiOperation.pas

@@ -100,6 +100,9 @@ Type
   private
     FData : TOpMultiOperationData;
     //
+    FtxSendersPubkeysUsedForSign : array of TAccountKey;
+    FchangesInfoPubkeysUsedForSign : array of TAccountKey;
+    //
     FSaveSignatureValue : Boolean;
     FTotalAmount : Int64;
     FTotalFee : Int64;
@@ -110,6 +113,7 @@ Type
     function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean; override;
     function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
     procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
+    procedure CopyUsedPubkeySignatureFrom(SourceOperation : TPCOperation); override;
   public
     function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
 
@@ -242,6 +246,25 @@ begin
   end;
 end;
 
+procedure TOpMultiOperation.CopyUsedPubkeySignatureFrom(SourceOperation: TPCOperation);
+var sourcemulti : TOpMultiOperation;
+  i : Integer;
+begin
+  inherited CopyUsedPubkeySignatureFrom(SourceOperation);
+  // Source MUST BE a TOpMultiOperation
+  if (SourceOperation is TOpMultiOperation) then begin
+    sourcemulti := TOpMultiOperation(SourceOperation);
+    SetLength(FtxSendersPubkeysUsedForSign,Length(sourcemulti.FtxSendersPubkeysUsedForSign));
+    for i:=0 to High(FtxSendersPubkeysUsedForSign) do begin
+      FtxSendersPubkeysUsedForSign[i] := sourcemulti.FtxSendersPubkeysUsedForSign[i];
+    end;
+    SetLength(FchangesInfoPubkeysUsedForSign,Length(sourcemulti.FchangesInfoPubkeysUsedForSign));
+    for i:=0 to High(FchangesInfoPubkeysUsedForSign) do begin
+      FchangesInfoPubkeysUsedForSign[i] := sourcemulti.FchangesInfoPubkeysUsedForSign[i];
+    end;
+  end else Raise Exception.Create('ERROR DEV 20181217-1 Source must be a TOpMultiOperation. Self:'+toString+' Source:'+SourceOperation.ToString);
+end;
+
 function TOpMultiOperation.IndexOfAccountChangeNameTo(const newName: AnsiString): Integer;
 begin
   If (newName<>'') then begin
@@ -262,6 +285,9 @@ begin
   for i:=0 to High(FData.changesInfo) do begin
     FData.changesInfo[i].Signature := CT_TECDSA_SIG_Nul;
   end;
+  FHasValidSignature:=False;
+  SetLength(FtxSendersPubkeysUsedForSign,0);
+  SetLength(FchangesInfoPubkeysUsedForSign,0);
 end;
 
 procedure TOpMultiOperation.InitializeData;
@@ -273,6 +299,8 @@ begin
   FSaveSignatureValue := True;
   FTotalAmount:=0;
   FTotalFee:=0;
+  SetLength(FtxSendersPubkeysUsedForSign,0);
+  SetLength(FchangesInfoPubkeysUsedForSign,0);
 end;
 
 function TOpMultiOperation.SaveOpToStream(Stream: TStream; SaveExtendedData: Boolean): Boolean;
@@ -350,6 +378,8 @@ begin
   FTotalAmount:=0;
   FTotalFee:=0;
   FHasValidSignature:=False;
+  SetLength(FtxSendersPubkeysUsedForSign,0);
+  SetLength(FchangesInfoPubkeysUsedForSign,0);
 
   SetLength(txsenders,0);
   SetLength(txreceivers,0);
@@ -446,13 +476,10 @@ var i : Integer;
 begin
   // Init
   SetLength(errors,0);
-  If FHasValidSignature then begin
-    // Will reuse FHasValidSignature if checked previously and was True
-    // Introduced on Build 4.0.2 to increase speed using MEMPOOL verified operations instead of verify again everytime
-    // Multioperations will not use standard TPCOperation.IsValidECDSASignature call because will need to check more than 1 signature
-    Result := True;
-    Exit;
-  end;
+  // Will reuse FHasValidSignature if checked previously and was True and was signed using same public keys
+  // Introduced on Build 4.0.2 to increase speed using MEMPOOL verified operations instead of verify again everytime
+  // Multioperations will not use standard TPCOperation.IsValidECDSASignature call because will need to check more than 1 signature
+  Result := False;
   // Do check it!
   Try
     ophtosign := GetDigestToSign(AccountTransaction.FreezedSafeBox.CurrentProtocol);
@@ -461,7 +488,12 @@ begin
       acc := AccountTransaction.Account(FData.txSenders[i].Account);
       If (length(FData.txSenders[i].Signature.r)>0) And
          (length(FData.txSenders[i].Signature.s)>0) then begin
-        If Not TCrypto.ECDSAVerify(acc.accountInfo.accountkey,ophtosign,FData.txSenders[i].Signature) then begin
+        If (FHasValidSignature) And (i<=High(FtxSendersPubkeysUsedForSign)) And (TAccountComp.EqualAccountKeys(FtxSendersPubkeysUsedForSign[i],acc.accountInfo.accountKey)) then begin
+          // Nothing to do, previously signed using same public key
+        end else If TCrypto.ECDSAVerify(acc.accountInfo.accountkey,ophtosign,FData.txSenders[i].Signature) then begin
+          if length(FtxSendersPubkeysUsedForSign)<=i then SetLength(FtxSendersPubkeysUsedForSign,i+1);
+          FtxSendersPubkeysUsedForSign[i] := acc.accountInfo.accountKey;
+        end else begin
           errors := Format('Invalid signature for sender %d/%d',[i+1,length(FData.txSenders)]);
           Exit;
         end;
@@ -475,7 +507,12 @@ begin
       acc := AccountTransaction.Account(FData.changesInfo[i].Account);
       If (length(FData.changesInfo[i].Signature.r)>0) And
          (length(FData.changesInfo[i].Signature.s)>0) then begin
-        If Not TCrypto.ECDSAVerify(acc.accountInfo.accountkey,ophtosign,FData.changesInfo[i].Signature) then begin
+        If (FHasValidSignature) And (i<=High(FchangesInfoPubkeysUsedForSign)) And (TAccountComp.EqualAccountKeys(FchangesInfoPubkeysUsedForSign[i],acc.accountInfo.accountKey)) then begin
+          // Nothing to do, previously signed using same public key
+        end else If TCrypto.ECDSAVerify(acc.accountInfo.accountkey,ophtosign,FData.changesInfo[i].Signature) then begin
+          if length(FchangesInfoPubkeysUsedForSign)<=i then SetLength(FchangesInfoPubkeysUsedForSign,i+1);
+          FchangesInfoPubkeysUsedForSign[i] := acc.accountInfo.accountKey;
+        end else begin
           errors := Format('Invalid signature for change info %d/%d',[i+1,length(FData.changesInfo)]);
           Exit;
         end;
@@ -485,9 +522,9 @@ begin
       end;
     end;
     // If here... all Ok
-    FHasValidSignature:=True;
+    Result:=True;
   finally
-    Result := FHasValidSignature;
+    FHasValidSignature := Result;
   end;
 end;