diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj
index ef931731d..926e0fd94 100644
--- a/crypto/BouncyCastle.Android.csproj
+++ b/crypto/BouncyCastle.Android.csproj
@@ -916,6 +916,7 @@
<Compile Include="src\crypto\parameters\NaccacheSternKeyGenerationParameters.cs" />
<Compile Include="src\crypto\parameters\NaccacheSternKeyParameters.cs" />
<Compile Include="src\crypto\parameters\NaccacheSternPrivateKeyParameters.cs" />
+ <Compile Include="src\crypto\parameters\ParametersWithID.cs" />
<Compile Include="src\crypto\parameters\ParametersWithIV.cs" />
<Compile Include="src\crypto\parameters\ParametersWithRandom.cs" />
<Compile Include="src\crypto\parameters\ParametersWithSBox.cs" />
@@ -951,6 +952,7 @@
<Compile Include="src\crypto\signers\PssSigner.cs" />
<Compile Include="src\crypto\signers\RandomDsaKCalculator.cs" />
<Compile Include="src\crypto\signers\RsaDigestSigner.cs" />
+ <Compile Include="src\crypto\signers\SM2Signer.cs" />
<Compile Include="src\crypto\signers\X931Signer.cs" />
<Compile Include="src\crypto\tls\AbstractTlsAgreementCredentials.cs" />
<Compile Include="src\crypto\tls\AbstractTlsCipherFactory.cs" />
diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj
index c30056db8..463ba2a1c 100644
--- a/crypto/BouncyCastle.csproj
+++ b/crypto/BouncyCastle.csproj
@@ -910,6 +910,7 @@
<Compile Include="src\crypto\parameters\NaccacheSternKeyGenerationParameters.cs" />
<Compile Include="src\crypto\parameters\NaccacheSternKeyParameters.cs" />
<Compile Include="src\crypto\parameters\NaccacheSternPrivateKeyParameters.cs" />
+ <Compile Include="src\crypto\parameters\ParametersWithID.cs" />
<Compile Include="src\crypto\parameters\ParametersWithIV.cs" />
<Compile Include="src\crypto\parameters\ParametersWithRandom.cs" />
<Compile Include="src\crypto\parameters\ParametersWithSBox.cs" />
@@ -945,6 +946,7 @@
<Compile Include="src\crypto\signers\PssSigner.cs" />
<Compile Include="src\crypto\signers\RandomDsaKCalculator.cs" />
<Compile Include="src\crypto\signers\RsaDigestSigner.cs" />
+ <Compile Include="src\crypto\signers\SM2Signer.cs" />
<Compile Include="src\crypto\signers\X931Signer.cs" />
<Compile Include="src\crypto\tls\AbstractTlsAgreementCredentials.cs" />
<Compile Include="src\crypto\tls\AbstractTlsCipherFactory.cs" />
diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj
index 1b004957b..783009d38 100644
--- a/crypto/BouncyCastle.iOS.csproj
+++ b/crypto/BouncyCastle.iOS.csproj
@@ -911,6 +911,7 @@
<Compile Include="src\crypto\parameters\NaccacheSternKeyGenerationParameters.cs" />
<Compile Include="src\crypto\parameters\NaccacheSternKeyParameters.cs" />
<Compile Include="src\crypto\parameters\NaccacheSternPrivateKeyParameters.cs" />
+ <Compile Include="src\crypto\parameters\ParametersWithID.cs" />
<Compile Include="src\crypto\parameters\ParametersWithIV.cs" />
<Compile Include="src\crypto\parameters\ParametersWithRandom.cs" />
<Compile Include="src\crypto\parameters\ParametersWithSBox.cs" />
@@ -946,6 +947,7 @@
<Compile Include="src\crypto\signers\PssSigner.cs" />
<Compile Include="src\crypto\signers\RandomDsaKCalculator.cs" />
<Compile Include="src\crypto\signers\RsaDigestSigner.cs" />
+ <Compile Include="src\crypto\signers\SM2Signer.cs" />
<Compile Include="src\crypto\signers\X931Signer.cs" />
<Compile Include="src\crypto\tls\AbstractTlsAgreementCredentials.cs" />
<Compile Include="src\crypto\tls\AbstractTlsCipherFactory.cs" />
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index a80ac39ea..509638ff1 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -4369,6 +4369,11 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "src\crypto\parameters\ParametersWithID.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "src\crypto\parameters\ParametersWithIV.cs"
SubType = "Code"
BuildAction = "Compile"
@@ -4614,6 +4619,11 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "src\crypto\signers\SM2Signer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "src\crypto\signers\X931Signer.cs"
SubType = "Code"
BuildAction = "Compile"
@@ -11910,6 +11920,11 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "test\src\crypto\test\SM2SignerTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "test\src\crypto\test\SM3DigestTest.cs"
SubType = "Code"
BuildAction = "Compile"
@@ -12795,6 +12810,11 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "test\src\util\test\TestRandomBigInteger.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "test\src\util\test\UncloseableStream.cs"
SubType = "Code"
BuildAction = "Compile"
diff --git a/crypto/src/crypto/parameters/ParametersWithID.cs b/crypto/src/crypto/parameters/ParametersWithID.cs
new file mode 100644
index 000000000..37f68705b
--- /dev/null
+++ b/crypto/src/crypto/parameters/ParametersWithID.cs
@@ -0,0 +1,36 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+ public class ParametersWithID
+ : ICipherParameters
+ {
+ private readonly ICipherParameters parameters;
+ private readonly byte[] id;
+
+ public ParametersWithID(ICipherParameters parameters,
+ byte[] id)
+ : this(parameters, id, 0, id.Length)
+ {
+ }
+
+ public ParametersWithID(ICipherParameters parameters,
+ byte[] id, int idOff, int idLen)
+ {
+ this.parameters = parameters;
+ this.id = Arrays.CopyOfRange(id, idOff, idOff + idLen);
+ }
+
+ public byte[] GetID()
+ {
+ return id;
+ }
+
+ public ICipherParameters Parameters
+ {
+ get { return parameters; }
+ }
+ }
+}
diff --git a/crypto/src/crypto/signers/SM2Signer.cs b/crypto/src/crypto/signers/SM2Signer.cs
new file mode 100644
index 000000000..c5a5981d8
--- /dev/null
+++ b/crypto/src/crypto/signers/SM2Signer.cs
@@ -0,0 +1,258 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Math.EC.Multiplier;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+ /// <summary>The SM2 Digital Signature algorithm.</summary>
+ public class SM2Signer
+ : ISigner
+ {
+ private readonly IDsaKCalculator kCalculator = new RandomDsaKCalculator();
+ private readonly SM3Digest digest = new SM3Digest();
+
+ private ECDomainParameters ecParams;
+ private ECPoint pubPoint;
+ private ECKeyParameters ecKey;
+ private byte[] z;
+
+ public virtual string AlgorithmName
+ {
+ get { return "SM2"; }
+ }
+
+ public virtual void Init(bool forSigning, ICipherParameters parameters)
+ {
+ ICipherParameters baseParam;
+ byte[] userID;
+
+ if (parameters is ParametersWithID)
+ {
+ baseParam = ((ParametersWithID)parameters).Parameters;
+ userID = ((ParametersWithID)parameters).GetID();
+ }
+ else
+ {
+ baseParam = parameters;
+ userID = Hex.Decode("31323334353637383132333435363738"); // the default value (ASCII "1234567812345678")
+ }
+
+ if (forSigning)
+ {
+ if (baseParam is ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)baseParam;
+
+ ecKey = (ECKeyParameters)rParam.Parameters;
+ ecParams = ecKey.Parameters;
+ kCalculator.Init(ecParams.N, rParam.Random);
+ }
+ else
+ {
+ ecKey = (ECKeyParameters)baseParam;
+ ecParams = ecKey.Parameters;
+ kCalculator.Init(ecParams.N, new SecureRandom());
+ }
+ pubPoint = CreateBasePointMultiplier().Multiply(ecParams.G, ((ECPrivateKeyParameters)ecKey).D).Normalize();
+ }
+ else
+ {
+ ecKey = (ECKeyParameters)baseParam;
+ ecParams = ecKey.Parameters;
+ pubPoint = ((ECPublicKeyParameters)ecKey).Q;
+ }
+
+ digest.Reset();
+ z = GetZ(userID);
+
+ digest.BlockUpdate(z, 0, z.Length);
+ }
+
+ public virtual void Update(byte b)
+ {
+ digest.Update(b);
+ }
+
+ public virtual void BlockUpdate(byte[] buf, int off, int len)
+ {
+ digest.BlockUpdate(buf, off, len);
+ }
+
+ public virtual bool VerifySignature(byte[] signature)
+ {
+ try
+ {
+ BigInteger[] rs = DerDecode(signature);
+ if (rs != null)
+ {
+ return VerifySignature(rs[0], rs[1]);
+ }
+ }
+ catch (IOException e)
+ {
+ }
+
+ return false;
+ }
+
+ public virtual void Reset()
+ {
+ if (z != null)
+ {
+ digest.Reset();
+ digest.BlockUpdate(z, 0, z.Length);
+ }
+ }
+
+ public virtual byte[] GenerateSignature()
+ {
+ byte[] eHash = DigestUtilities.DoFinal(digest);
+
+ BigInteger n = ecParams.N;
+ BigInteger e = CalculateE(eHash);
+ BigInteger d = ((ECPrivateKeyParameters)ecKey).D;
+
+ BigInteger r, s;
+
+ ECMultiplier basePointMultiplier = CreateBasePointMultiplier();
+
+ // 5.2.1 Draft RFC: SM2 Public Key Algorithms
+ do // generate s
+ {
+ BigInteger k;
+ do // generate r
+ {
+ // A3
+ k = kCalculator.NextK();
+
+ // A4
+ ECPoint p = basePointMultiplier.Multiply(ecParams.G, k).Normalize();
+
+ // A5
+ r = e.Add(p.AffineXCoord.ToBigInteger()).Mod(n);
+ }
+ while (r.SignValue == 0 || r.Add(k).Equals(n));
+
+ // A6
+ BigInteger dPlus1ModN = d.Add(BigInteger.One).ModInverse(n);
+
+ s = k.Subtract(r.Multiply(d)).Mod(n);
+ s = dPlus1ModN.Multiply(s).Mod(n);
+ }
+ while (s.SignValue == 0);
+
+ // A7
+ try
+ {
+ return DerEncode(r, s);
+ }
+ catch (IOException ex)
+ {
+ throw new CryptoException("unable to encode signature: " + ex.Message, ex);
+ }
+ }
+
+ private bool VerifySignature(BigInteger r, BigInteger s)
+ {
+ BigInteger n = ecParams.N;
+
+ // 5.3.1 Draft RFC: SM2 Public Key Algorithms
+ // B1
+ if (r.CompareTo(BigInteger.One) < 0 || r.CompareTo(n) >= 0)
+ return false;
+
+ // B2
+ if (s.CompareTo(BigInteger.One) < 0 || s.CompareTo(n) >= 0)
+ return false;
+
+ // B3
+ byte[] eHash = DigestUtilities.DoFinal(digest);
+
+ // B4
+ BigInteger e = CalculateE(eHash);
+
+ // B5
+ BigInteger t = r.Add(s).Mod(n);
+ if (t.SignValue == 0)
+ return false;
+
+ // B6
+ ECPoint q = ((ECPublicKeyParameters)ecKey).Q;
+ ECPoint x1y1 = ECAlgorithms.SumOfTwoMultiplies(ecParams.G, s, q, t).Normalize();
+ if (x1y1.IsInfinity)
+ return false;
+
+ // B7
+ return r.Equals(e.Add(x1y1.AffineXCoord.ToBigInteger()).Mod(n));
+ }
+
+ private byte[] GetZ(byte[] userID)
+ {
+ AddUserID(digest, userID);
+
+ AddFieldElement(digest, ecParams.Curve.A);
+ AddFieldElement(digest, ecParams.Curve.B);
+ AddFieldElement(digest, ecParams.G.AffineXCoord);
+ AddFieldElement(digest, ecParams.G.AffineYCoord);
+ AddFieldElement(digest, pubPoint.AffineXCoord);
+ AddFieldElement(digest, pubPoint.AffineYCoord);
+
+ return DigestUtilities.DoFinal(digest);
+ }
+
+ private void AddUserID(IDigest digest, byte[] userID)
+ {
+ int len = userID.Length * 8;
+ digest.Update((byte)(len >> 8));
+ digest.Update((byte)len);
+ digest.BlockUpdate(userID, 0, userID.Length);
+ }
+
+ private void AddFieldElement(IDigest digest, ECFieldElement v)
+ {
+ byte[] p = v.GetEncoded();
+ digest.BlockUpdate(p, 0, p.Length);
+ }
+
+ protected virtual BigInteger CalculateE(byte[] message)
+ {
+ return new BigInteger(1, message);
+ }
+
+ protected virtual ECMultiplier CreateBasePointMultiplier()
+ {
+ return new FixedPointCombMultiplier();
+ }
+
+ protected virtual BigInteger[] DerDecode(byte[] encoding)
+ {
+ Asn1Sequence seq = Asn1Sequence.GetInstance(Asn1Object.FromByteArray(encoding));
+ if (seq.Count != 2)
+ return null;
+
+ BigInteger r = DerInteger.GetInstance(seq[0]).Value;
+ BigInteger s = DerInteger.GetInstance(seq[1]).Value;
+
+ byte[] expectedEncoding = DerEncode(r, s);
+ if (!Arrays.ConstantTimeAreEqual(expectedEncoding, encoding))
+ return null;
+
+ return new BigInteger[]{ r, s };
+ }
+
+ protected virtual byte[] DerEncode(BigInteger r, BigInteger s)
+ {
+ return new DerSequence(new DerInteger(r), new DerInteger(s)).GetEncoded(Asn1Encodable.Der);
+ }
+ }
+}
diff --git a/crypto/test/UnitTests.csproj b/crypto/test/UnitTests.csproj
index e33b47464..f875c22b1 100644
--- a/crypto/test/UnitTests.csproj
+++ b/crypto/test/UnitTests.csproj
@@ -256,6 +256,7 @@
<Compile Include="src\crypto\test\SHA512HMacTest.cs" />
<Compile Include="src\crypto\test\SHA512t224DigestTest.cs" />
<Compile Include="src\crypto\test\SHA512t256DigestTest.cs" />
+ <Compile Include="src\crypto\test\SM2SignerTest.cs" />
<Compile Include="src\crypto\test\SM3DigestTest.cs" />
<Compile Include="src\crypto\test\SkeinDigestTest.cs" />
<Compile Include="src\crypto\test\SkeinMacTest.cs" />
@@ -440,6 +441,7 @@
<Compile Include="src\util\test\SimpleTest.cs" />
<Compile Include="src\util\test\SimpleTestResult.cs" />
<Compile Include="src\util\test\TestFailedException.cs" />
+ <Compile Include="src\util\test\TestRandomBigInteger.cs" />
<Compile Include="src\util\test\UncloseableStream.cs" />
<Compile Include="src\x509\test\TestCertificateGen.cs" />
</ItemGroup>
diff --git a/crypto/test/src/crypto/test/RegressionTest.cs b/crypto/test/src/crypto/test/RegressionTest.cs
index ba6c341d4..f2a92fab3 100644
--- a/crypto/test/src/crypto/test/RegressionTest.cs
+++ b/crypto/test/src/crypto/test/RegressionTest.cs
@@ -128,6 +128,7 @@ namespace Org.BouncyCastle.Crypto.Tests
new X931SignerTest(),
new KeccakDigestTest(),
new ShakeDigestTest(),
+ new SM2SignerTest(),
};
public static void Main(string[] args)
diff --git a/crypto/test/src/crypto/test/SM2SignerTest.cs b/crypto/test/src/crypto/test/SM2SignerTest.cs
new file mode 100644
index 000000000..a79793016
--- /dev/null
+++ b/crypto/test/src/crypto/test/SM2SignerTest.cs
@@ -0,0 +1,194 @@
+using System;
+using System.IO;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+ [TestFixture]
+ public class SM2SignerTest
+ : SimpleTest
+ {
+ public override string Name
+ {
+ get { return "SM2Signer"; }
+ }
+
+ private void DoSignerTestFp()
+ {
+ BigInteger SM2_ECC_P = new BigInteger("8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3", 16);
+ BigInteger SM2_ECC_A = new BigInteger("787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498", 16);
+ BigInteger SM2_ECC_B = new BigInteger("63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A", 16);
+ BigInteger SM2_ECC_N = new BigInteger("8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7", 16);
+ BigInteger SM2_ECC_GX = new BigInteger("421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D", 16);
+ BigInteger SM2_ECC_GY = new BigInteger("0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2", 16);
+
+ ECCurve curve = new FpCurve(SM2_ECC_P, SM2_ECC_A, SM2_ECC_B);
+
+ ECPoint g = curve.CreatePoint(SM2_ECC_GX, SM2_ECC_GY);
+ ECDomainParameters domainParams = new ECDomainParameters(curve, g, SM2_ECC_N);
+
+ ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParams, new TestRandomBigInteger("128B2FA8BD433C6C068C8D803DFF79792A519A55171B1B650C23661D15897263", 16));
+ ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
+
+ keyPairGenerator.Init(keyGenerationParams);
+ AsymmetricCipherKeyPair kp = keyPairGenerator.GenerateKeyPair();
+
+ ECPublicKeyParameters ecPub = (ECPublicKeyParameters)kp.Public;
+ ECPrivateKeyParameters ecPriv = (ECPrivateKeyParameters)kp.Private;
+
+ SM2Signer signer = new SM2Signer();
+
+ signer.Init(true,
+ new ParametersWithID(new ParametersWithRandom(ecPriv,
+ new TestRandomBigInteger("6CB28D99385C175C94F94E934817663FC176D925DD72B727260DBAAE1FB2F96F", 16)),
+ Strings.ToByteArray("ALICE123@YAHOO.COM")));
+
+ byte[] msg = Strings.ToByteArray("message digest");
+
+ signer.BlockUpdate(msg, 0, msg.Length);
+
+ byte[] sig = signer.GenerateSignature();
+
+ BigInteger[] rs = Decode(sig);
+
+ IsTrue("r wrong", rs[0].Equals(new BigInteger("40F1EC59F793D9F49E09DCEF49130D4194F79FB1EED2CAA55BACDB49C4E755D1", 16)));
+ IsTrue("s wrong", rs[1].Equals(new BigInteger("6FC6DAC32C5D5CF10C77DFB20F7C2EB667A457872FB09EC56327A67EC7DEEBE7", 16)));
+
+ signer.Init(false, new ParametersWithID(ecPub, Strings.ToByteArray("ALICE123@YAHOO.COM")));
+
+ signer.BlockUpdate(msg, 0, msg.Length);
+
+ IsTrue("verification failed", signer.VerifySignature(sig));
+ }
+
+ private void DoSignerTestF2m()
+ {
+ BigInteger SM2_ECC_A = new BigInteger("00", 16);
+ BigInteger SM2_ECC_B = new BigInteger("E78BCD09746C202378A7E72B12BCE00266B9627ECB0B5A25367AD1AD4CC6242B", 16);
+ BigInteger SM2_ECC_N = new BigInteger("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC972CF7E6B6F900945B3C6A0CF6161D", 16);
+ BigInteger SM2_ECC_GX = new BigInteger("00CDB9CA7F1E6B0441F658343F4B10297C0EF9B6491082400A62E7A7485735FADD", 16);
+ BigInteger SM2_ECC_GY = new BigInteger("013DE74DA65951C4D76DC89220D5F7777A611B1C38BAE260B175951DC8060C2B3E", 16);
+
+ ECCurve curve = new F2mCurve(257, 12, SM2_ECC_A, SM2_ECC_B);
+
+ ECPoint g = curve.CreatePoint(SM2_ECC_GX, SM2_ECC_GY);
+ ECDomainParameters domainParams = new ECDomainParameters(curve, g, SM2_ECC_N);
+
+ ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParams, new TestRandomBigInteger("771EF3DBFF5F1CDC32B9C572930476191998B2BF7CB981D7F5B39202645F0931", 16));
+ ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
+
+ keyPairGenerator.Init(keyGenerationParams);
+ AsymmetricCipherKeyPair kp = keyPairGenerator.GenerateKeyPair();
+
+ ECPublicKeyParameters ecPub = (ECPublicKeyParameters)kp.Public;
+ ECPrivateKeyParameters ecPriv = (ECPrivateKeyParameters)kp.Private;
+
+ SM2Signer signer = new SM2Signer();
+
+ signer.Init(true,
+ new ParametersWithID(new ParametersWithRandom(ecPriv,
+ new TestRandomBigInteger("36CD79FC8E24B7357A8A7B4A46D454C397703D6498158C605399B341ADA186D6", 16)),
+ Strings.ToByteArray("ALICE123@YAHOO.COM")));
+
+ byte[] msg = Strings.ToByteArray("message digest");
+
+ signer.BlockUpdate(msg, 0, msg.Length);
+
+ byte[] sig = signer.GenerateSignature();
+
+ BigInteger[] rs = Decode(sig);
+
+ IsTrue("F2m r wrong", rs[0].Equals(new BigInteger("6D3FBA26EAB2A1054F5D198332E335817C8AC453ED26D3391CD4439D825BF25B", 16)));
+ IsTrue("F2m s wrong", rs[1].Equals(new BigInteger("3124C5688D95F0A10252A9BED033BEC84439DA384621B6D6FAD77F94B74A9556", 16)));
+
+ signer.Init(false, new ParametersWithID(ecPub, Strings.ToByteArray("ALICE123@YAHOO.COM")));
+
+ signer.BlockUpdate(msg, 0, msg.Length);
+
+ IsTrue("verification failed", signer.VerifySignature(sig));
+ }
+
+ private void DoVerifyBoundsCheck()
+ {
+ BigInteger SM2_ECC_A = new BigInteger("00", 16);
+ BigInteger SM2_ECC_B = new BigInteger("E78BCD09746C202378A7E72B12BCE00266B9627ECB0B5A25367AD1AD4CC6242B", 16);
+ BigInteger SM2_ECC_N = new BigInteger("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC972CF7E6B6F900945B3C6A0CF6161D", 16);
+ BigInteger SM2_ECC_GX = new BigInteger("00CDB9CA7F1E6B0441F658343F4B10297C0EF9B6491082400A62E7A7485735FADD", 16);
+ BigInteger SM2_ECC_GY = new BigInteger("013DE74DA65951C4D76DC89220D5F7777A611B1C38BAE260B175951DC8060C2B3E", 16);
+
+ ECCurve curve = new F2mCurve(257, 12, SM2_ECC_A, SM2_ECC_B);
+
+ ECPoint g = curve.CreatePoint(SM2_ECC_GX, SM2_ECC_GY);
+ ECDomainParameters domainParams = new ECDomainParameters(curve, g, SM2_ECC_N);
+
+ ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParams, new TestRandomBigInteger("771EF3DBFF5F1CDC32B9C572930476191998B2BF7CB981D7F5B39202645F0931", 16));
+ ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
+
+ keyPairGenerator.Init(keyGenerationParams);
+ AsymmetricCipherKeyPair kp = keyPairGenerator.GenerateKeyPair();
+
+ ECPublicKeyParameters ecPub = (ECPublicKeyParameters)kp.Public;
+
+ SM2Signer signer = new SM2Signer();
+
+ signer.Init(false, ecPub);
+
+ signer.BlockUpdate(new byte[20], 0, 20);
+ IsTrue(!signer.VerifySignature(Encode(BigInteger.Zero, BigInteger.ValueOf(8))));
+
+ signer.BlockUpdate(new byte[20], 0, 20);
+ IsTrue(!signer.VerifySignature(Encode(BigInteger.ValueOf(8), BigInteger.Zero)));
+
+ signer.BlockUpdate(new byte[20], 0, 20);
+ IsTrue(!signer.VerifySignature(Encode(SM2_ECC_N, BigInteger.ValueOf(8))));
+
+ signer.BlockUpdate(new byte[20], 0, 20);
+ IsTrue(!signer.VerifySignature(Encode(BigInteger.ValueOf(8), SM2_ECC_N)));
+ }
+
+ public override void PerformTest()
+ {
+ DoSignerTestFp();
+ DoSignerTestF2m();
+ DoVerifyBoundsCheck();
+ }
+
+ private static BigInteger[] Decode(byte[] sig)
+ {
+ Asn1Sequence s = Asn1Sequence.GetInstance(sig);
+
+ return new BigInteger[] { DerInteger.GetInstance(s[0]).Value,
+ DerInteger.GetInstance(s[1]).Value };
+ }
+
+ private static byte[] Encode(BigInteger r, BigInteger s)
+ {
+ return new DerSequence(new DerInteger(r), new DerInteger(s)).GetEncoded();
+ }
+
+ public static void Main(
+ string[] args)
+ {
+ RunTest(new SM2SignerTest());
+ }
+
+ [Test]
+ public void TestFunction()
+ {
+ string resultText = Perform().ToString();
+
+ Assert.AreEqual(Name + ": Okay", resultText);
+ }
+ }
+}
diff --git a/crypto/test/src/util/test/FixedSecureRandom.cs b/crypto/test/src/util/test/FixedSecureRandom.cs
index d8598ac24..1368aa231 100644
--- a/crypto/test/src/util/test/FixedSecureRandom.cs
+++ b/crypto/test/src/util/test/FixedSecureRandom.cs
@@ -2,16 +2,90 @@ using System;
using System.IO;
using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.Encoders;
+
+using M = Org.BouncyCastle.Math;
namespace Org.BouncyCastle.Utilities.Test
{
public class FixedSecureRandom
: SecureRandom
{
+ private static readonly M.BigInteger REGULAR = new M.BigInteger("01020304ffffffff0506070811111111", 16);
+ private static readonly M.BigInteger ANDROID = new M.BigInteger("1111111105060708ffffffff01020304", 16);
+ private static readonly M.BigInteger CLASSPATH = new M.BigInteger("3020104ffffffff05060708111111", 16);
+
+ private static readonly bool isAndroidStyle;
+ private static readonly bool isClasspathStyle;
+ private static readonly bool isRegularStyle;
+
+ static FixedSecureRandom()
+ {
+ M.BigInteger check1 = new M.BigInteger(128, new RandomChecker());
+ M.BigInteger check2 = new M.BigInteger(120, new RandomChecker());
+
+ isAndroidStyle = check1.Equals(ANDROID);
+ isRegularStyle = check1.Equals(REGULAR);
+ isClasspathStyle = check2.Equals(CLASSPATH);
+ }
+
private byte[] _data;
private int _index;
- protected FixedSecureRandom(
+ /**
+ * Base class for sources of fixed "Randomness"
+ */
+ public class Source
+ {
+ internal byte[] data;
+
+ internal Source(byte[] data)
+ {
+ this.data = data;
+ }
+ }
+
+ /**
+ * Data Source - in this case we just expect requests for byte arrays.
+ */
+ public class Data
+ : Source
+ {
+ public Data(byte[] data)
+ : base(data)
+ {
+ }
+ }
+
+ /**
+ * BigInteger Source - in this case we expect requests for data that will be used
+ * for BigIntegers. The FixedSecureRandom will attempt to compensate for platform differences here.
+ */
+ public class BigInteger
+ : Source
+ {
+ public BigInteger(byte[] data)
+ : base(data)
+ {
+ }
+
+ public BigInteger(int bitLength, byte[] data)
+ : base(ExpandToBitLength(bitLength, data))
+ {
+ }
+
+ public BigInteger(string hexData)
+ : this(Hex.Decode(hexData))
+ {
+ }
+
+ public BigInteger(int bitLength, string hexData)
+ : base(ExpandToBitLength(bitLength, Hex.Decode(hexData)))
+ {
+ }
+ }
+
+ protected FixedSecureRandom(
byte[] data)
{
_data = data;
@@ -38,6 +112,103 @@ namespace Org.BouncyCastle.Utilities.Test
return new FixedSecureRandom(bOut.ToArray());
}
+ public FixedSecureRandom(
+ Source[] sources)
+ {
+ MemoryStream bOut = new MemoryStream();
+
+ if (isRegularStyle)
+ {
+ if (isClasspathStyle)
+ {
+ for (int i = 0; i != sources.Length; i++)
+ {
+ try
+ {
+ if (sources[i] is BigInteger)
+ {
+ byte[] data = sources[i].data;
+ int len = data.Length - (data.Length % 4);
+ for (int w = data.Length - len - 1; w >= 0; w--)
+ {
+ bOut.WriteByte(data[w]);
+ }
+ for (int w = data.Length - len; w < data.Length; w += 4)
+ {
+ bOut.Write(data, w, 4);
+ }
+ }
+ else
+ {
+ bOut.Write(sources[i].data, 0, sources[i].data.Length);
+ }
+ }
+ catch (IOException e)
+ {
+ throw new ArgumentException("can't save value source.");
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i != sources.Length; i++)
+ {
+ try
+ {
+ bOut.Write(sources[i].data, 0, sources[i].data.Length);
+ }
+ catch (IOException e)
+ {
+ throw new ArgumentException("can't save value source.");
+ }
+ }
+ }
+ }
+ else if (isAndroidStyle)
+ {
+ for (int i = 0; i != sources.Length; i++)
+ {
+ try
+ {
+ if (sources[i] is BigInteger)
+ {
+ byte[] data = sources[i].data;
+ int len = data.Length - (data.Length % 4);
+ for (int w = 0; w < len; w += 4)
+ {
+ bOut.Write(data, data.Length - (w + 4), 4);
+ }
+ if (data.Length - len != 0)
+ {
+ for (int w = 0; w != 4 - (data.Length - len); w++)
+ {
+ bOut.WriteByte(0);
+ }
+ }
+ for (int w = 0; w != data.Length - len; w++)
+ {
+ bOut.WriteByte(data[len + w]);
+ }
+ }
+ else
+ {
+ bOut.Write(sources[i].data, 0, sources[i].data.Length);
+ }
+ }
+ catch (IOException e)
+ {
+ throw new ArgumentException("can't save value source.");
+ }
+ }
+ }
+ else
+ {
+ throw new InvalidOperationException("Unrecognized BigInteger implementation");
+ }
+
+ _data = bOut.ToArray();
+ }
+
public override byte[] GenerateSeed(int numBytes)
{
return SecureRandom.GetNextBytes(this, numBytes);
@@ -65,5 +236,68 @@ namespace Org.BouncyCastle.Utilities.Test
{
get { return _index == _data.Length; }
}
+
+ private class RandomChecker
+ : SecureRandom
+ {
+ byte[] data = Hex.Decode("01020304ffffffff0506070811111111");
+ int index = 0;
+
+ public override void NextBytes(byte[] bytes)
+ {
+ Array.Copy(data, index, bytes, 0, bytes.Length);
+
+ index += bytes.Length;
+ }
+ }
+
+ private static byte[] ExpandToBitLength(int bitLength, byte[] v)
+ {
+ if ((bitLength + 7) / 8 > v.Length)
+ {
+ byte[] tmp = new byte[(bitLength + 7) / 8];
+
+ Array.Copy(v, 0, tmp, tmp.Length - v.Length, v.Length);
+ if (isAndroidStyle)
+ {
+ if (bitLength % 8 != 0)
+ {
+ uint i = BE_To_UInt32(tmp, 0);
+ UInt32_To_BE(i << (8 - (bitLength % 8)), tmp, 0);
+ }
+ }
+
+ return tmp;
+ }
+ else
+ {
+ if (isAndroidStyle && bitLength < (v.Length * 8))
+ {
+ if (bitLength % 8 != 0)
+ {
+ uint i = BE_To_UInt32(v, 0);
+ UInt32_To_BE(i << (8 - (bitLength % 8)), v, 0);
+ }
+ }
+ }
+
+ return v;
+ }
+
+ internal static uint BE_To_UInt32(byte[] bs, int off)
+ {
+ return (uint)bs[off] << 24
+ | (uint)bs[off + 1] << 16
+ | (uint)bs[off + 2] << 8
+ | (uint)bs[off + 3];
+ }
+
+ internal static void UInt32_To_BE(uint n, byte[] bs, int off)
+ {
+ bs[off] = (byte)(n >> 24);
+ bs[off + 1] = (byte)(n >> 16);
+ bs[off + 2] = (byte)(n >> 8);
+ bs[off + 3] = (byte)(n);
+ }
}
}
diff --git a/crypto/test/src/util/test/SimpleTest.cs b/crypto/test/src/util/test/SimpleTest.cs
index fea680832..154da27f4 100644
--- a/crypto/test/src/util/test/SimpleTest.cs
+++ b/crypto/test/src/util/test/SimpleTest.cs
@@ -27,6 +27,12 @@ namespace Org.BouncyCastle.Utilities.Test
throw new TestFailedException(SimpleTestResult.Failed(this, message));
}
+ internal void IsTrue(bool value)
+ {
+ if (!value)
+ throw new TestFailedException(SimpleTestResult.Failed(this, "no message"));
+ }
+
internal void IsTrue(string message, bool value)
{
if (!value)
diff --git a/crypto/test/src/util/test/TestRandomBigInteger.cs b/crypto/test/src/util/test/TestRandomBigInteger.cs
new file mode 100644
index 000000000..ef38293b9
--- /dev/null
+++ b/crypto/test/src/util/test/TestRandomBigInteger.cs
@@ -0,0 +1,55 @@
+using System;
+
+using M = Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Utilities.Test
+{
+ /**
+ * A fixed secure random designed to return data for someone needing to create a single BigInteger.
+ */
+ public class TestRandomBigInteger
+ : FixedSecureRandom
+ {
+ /**
+ * Constructor from a base 10 represention of a BigInteger.
+ *
+ * @param encoding a base 10 represention of a BigInteger.
+ */
+ public TestRandomBigInteger(string encoding)
+ : this(encoding, 10)
+ {
+ }
+
+ /**
+ * Constructor from a base radix represention of a BigInteger.
+ *
+ * @param encoding a String BigInteger of base radix.
+ * @param radix the radix to use.
+ */
+ public TestRandomBigInteger(string encoding, int radix)
+ : base(new FixedSecureRandom.Source[] { new FixedSecureRandom.BigInteger(BigIntegers.AsUnsignedByteArray(new M.BigInteger(encoding, radix))) })
+ {
+ }
+
+ /**
+ * Constructor based on a byte array.
+ *
+ * @param encoding a 2's complement representation of the BigInteger.
+ */
+ public TestRandomBigInteger(byte[] encoding)
+ : base(new FixedSecureRandom.Source[] { new FixedSecureRandom.BigInteger(encoding) })
+ {
+ }
+
+ /**
+ * Constructor which ensures encoding will produce a BigInteger from a request from the passed in bitLength.
+ *
+ * @param bitLength bit length for the BigInteger data request.
+ * @param encoding bytes making up the encoding.
+ */
+ public TestRandomBigInteger(int bitLength, byte[] encoding)
+ : base(new FixedSecureRandom.Source[] { new FixedSecureRandom.BigInteger(bitLength, encoding) })
+ {
+ }
+ }
+}
|