diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2017-09-16 11:39:55 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2017-09-16 11:39:55 +0700 |
commit | 67403751cdd80fd9b2b17489909c6178bbef1cc4 (patch) | |
tree | 55ca14ce3ac471fec0d5e7e7103495542087d6f3 | |
parent | Add class summary (diff) | |
download | BouncyCastle.NET-ed25519-67403751cdd80fd9b2b17489909c6178bbef1cc4.tar.xz |
Port of SM2Signer from Java
-rw-r--r-- | crypto/BouncyCastle.Android.csproj | 2 | ||||
-rw-r--r-- | crypto/BouncyCastle.csproj | 2 | ||||
-rw-r--r-- | crypto/BouncyCastle.iOS.csproj | 2 | ||||
-rw-r--r-- | crypto/crypto.csproj | 20 | ||||
-rw-r--r-- | crypto/src/crypto/parameters/ParametersWithID.cs | 36 | ||||
-rw-r--r-- | crypto/src/crypto/signers/SM2Signer.cs | 258 | ||||
-rw-r--r-- | crypto/test/UnitTests.csproj | 2 | ||||
-rw-r--r-- | crypto/test/src/crypto/test/RegressionTest.cs | 1 | ||||
-rw-r--r-- | crypto/test/src/crypto/test/SM2SignerTest.cs | 194 | ||||
-rw-r--r-- | crypto/test/src/util/test/FixedSecureRandom.cs | 236 | ||||
-rw-r--r-- | crypto/test/src/util/test/SimpleTest.cs | 6 | ||||
-rw-r--r-- | crypto/test/src/util/test/TestRandomBigInteger.cs | 55 |
12 files changed, 813 insertions, 1 deletions
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) }) + { + } + } +} |