Browse Source

correct bounds checking on ECNR signer

-added mod for "on edge signatures" so that they will verify, added message recovery method.
Ugochukwu Mmaduekwe 6 years ago
parent
commit
e63805728f

+ 85 - 0
CryptoLib.Tests/src/Math/ECNRTests.pas

@@ -35,15 +35,23 @@ uses
   ClpIAsn1Objects,
   ClpIAsn1Objects,
   ClpIECC,
   ClpIECC,
   ClpECC,
   ClpECC,
+  ClpIDigest,
   ClpECNRSigner,
   ClpECNRSigner,
   ClpIECNRSigner,
   ClpIECNRSigner,
   ClpECDomainParameters,
   ClpECDomainParameters,
   ClpIECDomainParameters,
   ClpIECDomainParameters,
+  ClpIECKeyGenerationParameters,
+  ClpECKeyGenerationParameters,
   ClpECPrivateKeyParameters,
   ClpECPrivateKeyParameters,
   ClpIECPrivateKeyParameters,
   ClpIECPrivateKeyParameters,
   ClpECPublicKeyParameters,
   ClpECPublicKeyParameters,
   ClpIECPublicKeyParameters,
   ClpIECPublicKeyParameters,
+  ClpECKeyPairGenerator,
+  ClpIECKeyPairGenerator,
   ClpISigner,
   ClpISigner,
+  ClpIX9ECParameters,
+  ClpIAsymmetricCipherKeyPair,
+  ClpECNamedCurveTable,
   ClpParametersWithRandom,
   ClpParametersWithRandom,
   ClpIParametersWithRandom,
   ClpIParametersWithRandom,
   ClpSecureRandom,
   ClpSecureRandom,
@@ -51,8 +59,10 @@ uses
   ClpFixedSecureRandom,
   ClpFixedSecureRandom,
   ClpSignerUtilities,
   ClpSignerUtilities,
   ClpBigInteger,
   ClpBigInteger,
+  ClpBigIntegers,
   ClpConverters,
   ClpConverters,
   ClpCryptoLibTypes,
   ClpCryptoLibTypes,
+  ClpDigestUtilities,
   CryptoLibTestBase;
   CryptoLibTestBase;
 
 
 type
 type
@@ -121,6 +131,8 @@ type
     /// </summary>
     /// </summary>
     procedure TestECNR521bitPrimeSHA512; // SecP521r1
     procedure TestECNR521bitPrimeSHA512; // SecP521r1
 
 
+    procedure TestRange;
+
   end;
   end;
 
 
 implementation
 implementation
@@ -462,6 +474,79 @@ begin
   DoCheckSignature(521, priKey, pubKey, sgr, k, &message, r, s);
   DoCheckSignature(521, priKey, pubKey, sgr, k, &message, r, s);
 end;
 end;
 
 
+procedure TTestECNR.TestRange;
+var
+  myGenerator: IECKeyPairGenerator;
+  myRandom: ISecureRandom;
+  myCurve: String;
+  x9: IX9ECParameters;
+  myDomain: IECDomainParameters;
+  myParams: IECKeyGenerationParameters;
+  myPair: IAsymmetricCipherKeyPair;
+  myDigest: IDigest;
+  myArtifact, myMessage, msg: TCryptoLibByteArray;
+  signer: IECNRSigner;
+  order: TBigInteger;
+  sig: TCryptoLibGenericArray<TBigInteger>;
+begin
+  // Create the generator
+  myGenerator := TECKeyPairGenerator.Create();
+  myRandom := TSecureRandom.Create();
+  myCurve := 'brainpoolP192t1';
+
+  // Lookup the parameters
+  x9 := TECNamedCurveTable.GetByName(myCurve);
+
+  // Initialise the generator
+  myDomain := TECDomainParameters.Create(x9.curve, x9.G, x9.n, x9.H,
+    x9.GetSeed());
+  myParams := TECKeyGenerationParameters.Create(myDomain, myRandom);
+  myGenerator.Init(myParams);
+
+  // Create the key Pair
+  myPair := myGenerator.GenerateKeyPair();
+
+  // Create the digest and the output buffer
+  myDigest := TDigestUtilities.GetDigest('TIGER');
+  System.SetLength(myArtifact, myDigest.GetDigestSize);
+  myMessage := TConverters.ConvertStringToBytes
+    ('Hello there. How is life treating you?', TEncoding.ASCII);
+
+  myDigest.BlockUpdate(myMessage, 0, System.Length(myMessage));
+  myDigest.DoFinal(myArtifact, 0);
+
+  // Create signer
+  signer := TECNRSigner.Create();
+  signer.Init(true, myPair.Private);
+
+  try
+    signer.GenerateSignature(myArtifact);
+    Fail('out of range input not caught');
+  except
+    on e: EDataLengthCryptoLibException do
+    begin
+      CheckEquals(e.Message, 'Input Too Large For ECNR Key.');
+    end;
+
+  end;
+
+  //
+  // check upper bound
+  order := (myPair.Public as IECPublicKeyParameters).parameters.n;
+
+  signer.Init(true, myPair.Private);
+  msg := TBigIntegers.AsUnsignedByteArray(order.Subtract(TBigInteger.One));
+  sig := signer.GenerateSignature(msg);
+
+  signer.Init(false, myPair.getPublic());
+  if (not signer.VerifySignature(msg, sig[0], sig[1])) then
+  begin
+    Fail('ECNR failed 2');
+  end;
+
+  CheckTrue(AreEqual(msg, signer.getRecoveredMessage(sig[0], sig[1])));
+end;
+
 initialization
 initialization
 
 
 // Register any test cases with the test runner
 // Register any test cases with the test runner

+ 3 - 1
CryptoLib.Tests/src/Math/ECPointTests.pas

@@ -78,7 +78,9 @@ type
   // */
   // */
   TF2m = class
   TF2m = class
 
 
-  public const
+  public
+
+    const
     // Irreducible polynomial for TPB z^4 + z + 1
     // Irreducible polynomial for TPB z^4 + z + 1
     m = Int32(4);
     m = Int32(4);
 
 

+ 102 - 45
CryptoLib/src/Crypto/Signers/ClpECNRSigner.pas

@@ -27,6 +27,7 @@ uses
   ClpIECC,
   ClpIECC,
   ClpIECNRSigner,
   ClpIECNRSigner,
   ClpBigInteger,
   ClpBigInteger,
+  ClpBigIntegers,
   ClpISecureRandom,
   ClpISecureRandom,
   ClpIECKeyParameters,
   ClpIECKeyParameters,
   ClpIParametersWithRandom,
   ClpIParametersWithRandom,
@@ -47,29 +48,45 @@ resourcestring
   SECPrivateKeyNotFound = 'EC Private Key Required for Signing';
   SECPrivateKeyNotFound = 'EC Private Key Required for Signing';
   SNotInitializedForSigning = 'Not Initialised For Signing';
   SNotInitializedForSigning = 'Not Initialised For Signing';
   SNotInitializedForVerifying = 'Not Initialised For Verifying';
   SNotInitializedForVerifying = 'Not Initialised For Verifying';
+  SNotInitializedForVerifyingRecovery =
+    'Not Initialised For Verifying/Recovery';
   SInputTooLargeForECNRKey = 'Input Too Large For ECNR Key.';
   SInputTooLargeForECNRKey = 'Input Too Large For ECNR Key.';
 
 
 type
 type
 
 
   /// <summary>
   /// <summary>
-  /// EC-NR as described in IEEE 1363-2000
+  /// EC-NR as described in IEEE 1363-2000 - a signature algorithm for Elliptic Curve which
+  /// also offers message recovery.
   /// </summary>
   /// </summary>
   TECNRSigner = class sealed(TInterfacedObject, IDsaExt, IECNRSigner)
   TECNRSigner = class sealed(TInterfacedObject, IDsaExt, IECNRSigner)
 
 
   strict private
   strict private
   var
   var
-    FforSigning: Boolean;
-    Fkey: IECKeyParameters;
-    Frandom: ISecureRandom;
+    FForSigning: Boolean;
+    FKey: IECKeyParameters;
+    FRandom: ISecureRandom;
 
 
-    function GetAlgorithmName: String; virtual;
-    function GetOrder: TBigInteger; virtual;
+    function GetAlgorithmName: String;
+    function GetOrder: TBigInteger;
+
+    function ExtractT(const pubKey: IECPublicKeyParameters;
+      const r, s: TBigInteger): TBigInteger;
 
 
   public
   public
 
 
     property Order: TBigInteger read GetOrder;
     property Order: TBigInteger read GetOrder;
     property AlgorithmName: String read GetAlgorithmName;
     property AlgorithmName: String read GetAlgorithmName;
 
 
+    /// <summary>
+    /// Initialise the signer.
+    /// </summary>
+    /// <param name="forSigning">
+    /// forSigning true if we are generating a signature, false for
+    /// verification or if we want to use the signer for message recovery.
+    /// </param>
+    /// <param name="parameters">
+    /// key parameters for signature generation.
+    /// </param>
     procedure Init(forSigning: Boolean;
     procedure Init(forSigning: Boolean;
       const parameters: ICipherParameters); virtual;
       const parameters: ICipherParameters); virtual;
 
 
@@ -121,23 +138,72 @@ type
     function VerifySignature(const &message: TCryptoLibByteArray;
     function VerifySignature(const &message: TCryptoLibByteArray;
       const r, s: TBigInteger): Boolean;
       const r, s: TBigInteger): Boolean;
 
 
+    /// <summary>
+    /// Returns the data used for the signature generation, assuming the
+    /// public key passed to Init() is correct.
+    /// </summary>
+    /// <returns>
+    /// null if r and s are not valid.
+    /// </returns>
+    function GetRecoveredMessage(const r, s: TBigInteger): TCryptoLibByteArray;
+
   end;
   end;
 
 
 implementation
 implementation
 
 
 { TECNRSigner }
 { TECNRSigner }
 
 
+function TECNRSigner.ExtractT(const pubKey: IECPublicKeyParameters;
+  const r, s: TBigInteger): TBigInteger;
+var
+  n, x: TBigInteger;
+  G, W, P: IECPoint;
+begin
+  n := pubKey.parameters.n;
+
+  // r in the range [1,n-1]
+  if ((r.CompareTo(TBigInteger.ONE) < 0) or (r.CompareTo(n) >= 0)) then
+  begin
+    result := Default (TBigInteger);
+    Exit;
+  end;
+
+  // s in the range [0,n-1]           NB: ECNR spec says 0
+  if ((s.CompareTo(TBigInteger.ZERO) < 0) or (s.CompareTo(n) >= 0)) then
+  begin
+    result := Default (TBigInteger);
+    Exit;
+  end;
+
+  // compute P = sG + rW
+
+  G := pubKey.parameters.G;
+  W := pubKey.Q;
+  // calculate P using Bouncy math
+  P := TECAlgorithms.SumOfTwoMultiplies(G, s, W, r).Normalize();
+
+  // components must be bogus.
+  if (P.IsInfinity) then
+  begin
+    result := Default (TBigInteger);
+    Exit;
+  end;
+
+  x := P.AffineXCoord.ToBigInteger();
+
+  result := r.Subtract(x).&Mod(n);
+end;
+
 function TECNRSigner.GenerateSignature(const &message: TCryptoLibByteArray)
 function TECNRSigner.GenerateSignature(const &message: TCryptoLibByteArray)
   : TCryptoLibGenericArray<TBigInteger>;
   : TCryptoLibGenericArray<TBigInteger>;
 var
 var
   n, e, r, s, Vx, x, u: TBigInteger;
   n, e, r, s, Vx, x, u: TBigInteger;
-  nBitLength, eBitLength: Int32;
   privKey: IECPrivateKeyParameters;
   privKey: IECPrivateKeyParameters;
   tempPair: IAsymmetricCipherKeyPair;
   tempPair: IAsymmetricCipherKeyPair;
   keyGen: IECKeyPairGenerator;
   keyGen: IECKeyPairGenerator;
   V: IECPublicKeyParameters;
   V: IECPublicKeyParameters;
 begin
 begin
-  if (not FforSigning) then
+  if (not FForSigning) then
   begin
   begin
     // not properly initialized... deal with it
     // not properly initialized... deal with it
     raise EInvalidOperationCryptoLibException.CreateRes
     raise EInvalidOperationCryptoLibException.CreateRes
@@ -145,14 +211,12 @@ begin
   end;
   end;
 
 
   n := Order;
   n := Order;
-  nBitLength := n.BitLength;
 
 
   e := TBigInteger.Create(1, &message);
   e := TBigInteger.Create(1, &message);
-  eBitLength := e.BitLength;
 
 
-  privKey := Fkey as IECPrivateKeyParameters;
+  privKey := FKey as IECPrivateKeyParameters;
 
 
-  if (eBitLength > nBitLength) then
+  if (e.CompareTo(n) >= 0) then
   begin
   begin
     raise EDataLengthCryptoLibException.CreateRes(@SInputTooLargeForECNRKey);
     raise EDataLengthCryptoLibException.CreateRes(@SInputTooLargeForECNRKey);
   end;
   end;
@@ -162,7 +226,7 @@ begin
     // the same EC parameters
     // the same EC parameters
     keyGen := TECKeyPairGenerator.Create();
     keyGen := TECKeyPairGenerator.Create();
 
 
-    keyGen.Init(TECKeyGenerationParameters.Create(privKey.parameters, Frandom)
+    keyGen.Init(TECKeyGenerationParameters.Create(privKey.parameters, FRandom)
       as IECKeyGenerationParameters);
       as IECKeyGenerationParameters);
 
 
     tempPair := keyGen.GenerateKeyPair();
     tempPair := keyGen.GenerateKeyPair();
@@ -189,7 +253,7 @@ end;
 
 
 function TECNRSigner.GetOrder: TBigInteger;
 function TECNRSigner.GetOrder: TBigInteger;
 begin
 begin
-  result := Fkey.parameters.n;
+  result := FKey.parameters.n;
 end;
 end;
 
 
 procedure TECNRSigner.Init(forSigning: Boolean;
 procedure TECNRSigner.Init(forSigning: Boolean;
@@ -198,19 +262,19 @@ var
   rParam: IParametersWithRandom;
   rParam: IParametersWithRandom;
   Lparameters: ICipherParameters;
   Lparameters: ICipherParameters;
 begin
 begin
-  FforSigning := forSigning;
+  FForSigning := forSigning;
   Lparameters := parameters;
   Lparameters := parameters;
   if (forSigning) then
   if (forSigning) then
   begin
   begin
 
 
     if (Supports(Lparameters, IParametersWithRandom, rParam)) then
     if (Supports(Lparameters, IParametersWithRandom, rParam)) then
     begin
     begin
-      Frandom := rParam.random;
+      FRandom := rParam.random;
       Lparameters := rParam.parameters;
       Lparameters := rParam.parameters;
     end
     end
     else
     else
     begin
     begin
-      Frandom := TSecureRandom.Create();
+      FRandom := TSecureRandom.Create();
     end;
     end;
 
 
     if (not(Supports(Lparameters, IECPrivateKeyParameters))) then
     if (not(Supports(Lparameters, IECPrivateKeyParameters))) then
@@ -218,7 +282,7 @@ begin
       raise EInvalidKeyCryptoLibException.CreateRes(@SECPrivateKeyNotFound);
       raise EInvalidKeyCryptoLibException.CreateRes(@SECPrivateKeyNotFound);
     end;
     end;
 
 
-    Fkey := Lparameters as IECPrivateKeyParameters;
+    FKey := Lparameters as IECPrivateKeyParameters;
   end
   end
   else
   else
   begin
   begin
@@ -227,7 +291,7 @@ begin
       raise EInvalidKeyCryptoLibException.CreateRes(@SECPublicKeyNotFound);
       raise EInvalidKeyCryptoLibException.CreateRes(@SECPublicKeyNotFound);
     end;
     end;
 
 
-    Fkey := Lparameters as IECPublicKeyParameters;
+    FKey := Lparameters as IECPublicKeyParameters;
   end;
   end;
 end;
 end;
 
 
@@ -235,18 +299,17 @@ function TECNRSigner.VerifySignature(const &message: TCryptoLibByteArray;
   const r, s: TBigInteger): Boolean;
   const r, s: TBigInteger): Boolean;
 var
 var
   pubKey: IECPublicKeyParameters;
   pubKey: IECPublicKeyParameters;
-  n, e, x, t: TBigInteger;
+  n, e, t: TBigInteger;
   nBitLength, eBitLength: Int32;
   nBitLength, eBitLength: Int32;
-  G, W, P: IECPoint;
 begin
 begin
-  if (FforSigning) then
+  if (FForSigning) then
   begin
   begin
     // not properly initialized... deal with it
     // not properly initialized... deal with it
     raise EInvalidOperationCryptoLibException.CreateRes
     raise EInvalidOperationCryptoLibException.CreateRes
       (@SNotInitializedForVerifying);
       (@SNotInitializedForVerifying);
   end;
   end;
 
 
-  pubKey := Fkey as IECPublicKeyParameters;
+  pubKey := FKey as IECPublicKeyParameters;
   n := pubKey.parameters.n;
   n := pubKey.parameters.n;
   nBitLength := n.BitLength;
   nBitLength := n.BitLength;
 
 
@@ -258,37 +321,31 @@ begin
     raise EDataLengthCryptoLibException.CreateRes(@SInputTooLargeForECNRKey);
     raise EDataLengthCryptoLibException.CreateRes(@SInputTooLargeForECNRKey);
   end;
   end;
 
 
-  // r in the range [1,n-1]
-  if ((r.CompareTo(TBigInteger.One) < 0) or (r.CompareTo(n) >= 0)) then
-  begin
-    result := false;
-    Exit;
-  end;
+  t := ExtractT(pubKey, r, s);
 
 
-  // s in the range [0,n-1]           NB: ECNR spec says 0
-  if ((s.CompareTo(TBigInteger.Zero) < 0) or (s.CompareTo(n) >= 0)) then
+  result := (t.IsInitialized) and (t.Equals(e.&Mod(n)));
+end;
+
+function TECNRSigner.GetRecoveredMessage(const r, s: TBigInteger)
+  : TCryptoLibByteArray;
+var
+  t: TBigInteger;
+begin
+  if (FForSigning) then
   begin
   begin
-    result := false;
-    Exit;
+    raise EInvalidOperationCryptoLibException.CreateRes
+      (@SNotInitializedForVerifyingRecovery);
   end;
   end;
 
 
-  // compute P = sG + rW
+  t := ExtractT(FKey as IECPublicKeyParameters, r, s);
 
 
-  G := pubKey.parameters.G;
-  W := pubKey.Q;
-  // calculate P using ECAlgorithms Math
-  P := TECAlgorithms.SumOfTwoMultiplies(G, s, W, r).Normalize();
-
-  if (P.IsInfinity) then
+  if (t.IsInitialized) then
   begin
   begin
-    result := false;
+    result := TBigIntegers.AsUnsignedByteArray(t);
     Exit;
     Exit;
   end;
   end;
 
 
-  x := P.AffineXCoord.ToBigInteger();
-  t := r.Subtract(x).&Mod(n);
-
-  result := t.Equals(e);
+  result := Nil;
 end;
 end;
 
 
 end.
 end.

+ 2 - 0
CryptoLib/src/Interfaces/ClpIECNRSigner.pas

@@ -30,6 +30,8 @@ type
   IECNRSigner = interface(IDsaExt)
   IECNRSigner = interface(IDsaExt)
     ['{C136F005-404E-4022-886E-DE5EFCECFF9C}']
     ['{C136F005-404E-4022-886E-DE5EFCECFF9C}']
 
 
+    function GetRecoveredMessage(const r, s: TBigInteger): TCryptoLibByteArray;
+
   end;
   end;
 
 
 implementation
 implementation