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 @@ public virtual void Init(bool forSigning, ICipherParameters parameters) 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 input) { + CheckData(); + digest.BlockUpdate(input); } #endif @@ -125,6 +138,8 @@ public virtual void BlockUpdate(ReadOnlySpan input) public virtual byte[] GenerateSignature() { + CheckData(); + byte[] eHash = DigestUtilities.DoFinal(digest); BigInteger n = ecParams.N; @@ -169,10 +184,16 @@ public virtual byte[] GenerateSignature() { 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 @@ public virtual bool VerifySignature(byte[] signature) 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 @@ private bool VerifySignature(BigInteger r, BigInteger s) 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; @@ -135,6 +132,12 @@ private void DoSignerTest(string curveName, IDigest d, string ident, string msg, } 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); @@ -162,6 +165,39 @@ private void DoSignerTest(ECDomainParameters domainParams, IDigest d, string ide 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;