summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2024-01-29 21:23:42 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2024-01-29 21:23:42 +0700
commit196797d73f1b675e1e6b49ea538c8e22ba3b2a5f (patch)
treea0c3e65ebdeacc0a64fb7b9dc6448a38ca639697
parentAdd Prehash digest for safer raw signers (diff)
downloadBouncyCastle.NET-ed25519-196797d73f1b675e1e6b49ea538c8e22ba3b2a5f.tar.xz
Support signer reuse in SM2Signer
-rw-r--r--crypto/src/crypto/signers/SM2Signer.cs62
-rw-r--r--crypto/test/src/crypto/test/SM2SignerTest.cs42
2 files changed, 95 insertions, 9 deletions
diff --git a/crypto/src/crypto/signers/SM2Signer.cs b/crypto/src/crypto/signers/SM2Signer.cs
index cd4b2d554..10244a7f3 100644
--- a/crypto/src/crypto/signers/SM2Signer.cs
+++ b/crypto/src/crypto/signers/SM2Signer.cs
@@ -15,10 +15,18 @@ namespace Org.BouncyCastle.Crypto.Signers
     public class SM2Signer
         : ISigner
     {
+        private enum State
+        {
+            Uninitialized = 0,
+            Init          = 1,
+            Data          = 2,
+        }
+
         private readonly IDsaKCalculator kCalculator = new RandomDsaKCalculator();
         private readonly IDigest digest;
         private readonly IDsaEncoding encoding;
 
+        private State m_state = State.Uninitialized;
         private ECDomainParameters ecParams;
         private ECPoint pubPoint;
         private ECKeyParameters ecKey;
@@ -100,23 +108,28 @@ namespace Org.BouncyCastle.Crypto.Signers
 
             digest.Reset();
             z = GetZ(userID);
-
-            digest.BlockUpdate(z, 0, z.Length);
+            m_state = State.Init;
         }
 
         public virtual void Update(byte b)
         {
+            CheckData();
+
             digest.Update(b);
         }
 
         public virtual void BlockUpdate(byte[] input, int inOff, int inLen)
         {
+            CheckData();
+
             digest.BlockUpdate(input, inOff, inLen);
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         public virtual void BlockUpdate(ReadOnlySpan<byte> input)
         {
+            CheckData();
+
             digest.BlockUpdate(input);
         }
 #endif
@@ -125,6 +138,8 @@ namespace Org.BouncyCastle.Crypto.Signers
 
         public virtual byte[] GenerateSignature()
         {
+            CheckData();
+
             byte[] eHash = DigestUtilities.DoFinal(digest);
 
             BigInteger n = ecParams.N;
@@ -169,10 +184,16 @@ namespace Org.BouncyCastle.Crypto.Signers
             {
                 throw new CryptoException("unable to encode signature: " + ex.Message, ex);
             }
+            finally
+            {
+                Reset();
+            }
         }
 
         public virtual bool VerifySignature(byte[] signature)
         {
+            CheckData();
+
             try
             {
                 BigInteger[] rs = encoding.Decode(ecParams.N, signature);
@@ -182,17 +203,28 @@ namespace Org.BouncyCastle.Crypto.Signers
             catch (Exception)
             {
             }
+            finally
+            {
+                Reset();
+            }
 
             return false;
         }
 
         public virtual void Reset()
         {
-            if (z != null)
+            switch (m_state)
             {
-                digest.Reset();
-                digest.BlockUpdate(z, 0, z.Length);
+            case State.Init:
+                return;
+            case State.Data:
+                break;
+            default:
+                throw new InvalidOperationException(AlgorithmName + " needs to be initialized");
             }
+
+            digest.Reset();
+            m_state = State.Init;
         }
 
         private bool VerifySignature(BigInteger r, BigInteger s)
@@ -226,7 +258,25 @@ namespace Org.BouncyCastle.Crypto.Signers
                 return false;
 
             // B7
-            return r.Equals(e.Add(x1y1.AffineXCoord.ToBigInteger()).Mod(n));
+            BigInteger expectedR = e.Add(x1y1.AffineXCoord.ToBigInteger()).Mod(n);
+
+            return expectedR.Equals(r);
+        }
+
+        private void CheckData()
+        {
+            switch (m_state)
+            {
+            case State.Init:
+                break;
+            case State.Data:
+                return;
+            default:
+                throw new InvalidOperationException(AlgorithmName + " needs to be initialized");
+            }
+
+            digest.BlockUpdate(z, 0, z.Length);
+            m_state = State.Data;
         }
 
         private byte[] GetZ(byte[] userID)
diff --git a/crypto/test/src/crypto/test/SM2SignerTest.cs b/crypto/test/src/crypto/test/SM2SignerTest.cs
index a8cc016fd..e9d40fdc6 100644
--- a/crypto/test/src/crypto/test/SM2SignerTest.cs
+++ b/crypto/test/src/crypto/test/SM2SignerTest.cs
@@ -1,6 +1,3 @@
-using System;
-using System.IO;
-
 using NUnit.Framework;
 
 using Org.BouncyCastle.Asn1;
@@ -136,6 +133,12 @@ namespace Org.BouncyCastle.Crypto.Tests
 
         private void DoSignerTest(ECDomainParameters domainParams, IDigest d, string ident, string msg, string x, string nonce, string r, string s)
         {
+            ImplSignerTest(domainParams, d, ident, msg, x, nonce, r, s);
+            ImplSignerTestReuse(domainParams, d, ident, msg, x);
+        }
+
+        private void ImplSignerTest(ECDomainParameters domainParams, IDigest d, string ident, string msg, string x, string nonce, string r, string s)
+        {
             byte[] idBytes = Strings.ToByteArray(ident);
             byte[] msgBytes = Strings.ToByteArray(msg);
             AsymmetricCipherKeyPair kp = GenerateKeyPair(domainParams, x);
@@ -162,6 +165,39 @@ namespace Org.BouncyCastle.Crypto.Tests
             IsTrue("verification failed", signer.VerifySignature(sig));
         }
 
+        private void ImplSignerTestReuse(ECDomainParameters domainParams, IDigest d, string ident, string msg, string x)
+        {
+            byte[] idBytes = Strings.ToByteArray(ident);
+            byte[] msgBytes = Strings.ToByteArray(msg);
+            AsymmetricCipherKeyPair kp = GenerateKeyPair(domainParams, x);
+
+            SM2Signer signer = new SM2Signer(d);
+
+            signer.Init(true, new ParametersWithID(kp.Private, idBytes));
+            signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
+            byte[] sig1 = signer.GenerateSignature();
+
+            signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
+            byte[] sig2 = signer.GenerateSignature();
+
+            signer.Update(0x00);
+            signer.Reset();
+            signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
+            byte[] sig3 = signer.GenerateSignature();
+
+            signer.Init(false, new ParametersWithID(kp.Public, idBytes));
+            signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
+            IsTrue("verification failed", signer.VerifySignature(sig1));
+
+            signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
+            IsTrue("verification failed", signer.VerifySignature(sig2));
+
+            signer.Update(0x00);
+            signer.Reset();
+            signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
+            IsTrue("verification failed", signer.VerifySignature(sig3));
+        }
+
         private void DoVerifyBoundsCheck()
         {
             ECDomainParameters domainParams = ParametersF2m;