summary refs log tree commit diff
path: root/crypto/test
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2017-09-16 11:39:55 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2017-09-16 11:39:55 +0700
commit67403751cdd80fd9b2b17489909c6178bbef1cc4 (patch)
tree55ca14ce3ac471fec0d5e7e7103495542087d6f3 /crypto/test
parentAdd class summary (diff)
downloadBouncyCastle.NET-ed25519-67403751cdd80fd9b2b17489909c6178bbef1cc4.tar.xz
Port of SM2Signer from Java
Diffstat (limited to 'crypto/test')
-rw-r--r--crypto/test/UnitTests.csproj2
-rw-r--r--crypto/test/src/crypto/test/RegressionTest.cs1
-rw-r--r--crypto/test/src/crypto/test/SM2SignerTest.cs194
-rw-r--r--crypto/test/src/util/test/FixedSecureRandom.cs236
-rw-r--r--crypto/test/src/util/test/SimpleTest.cs6
-rw-r--r--crypto/test/src/util/test/TestRandomBigInteger.cs55
6 files changed, 493 insertions, 1 deletions
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) })
+        {
+        }
+    }
+}