public static void GetPublicKey(Span <byte> publicKey, ReadOnlySpan <byte> privateKey)
        {
            if (publicKey.Length != PublicKeySizeInBytes)
            {
                throw new ArgumentException("privateKey.Count must be 32");
            }
            if (privateKey.Length != PrivateKeySizeInBytes)
            {
                throw new ArgumentException("privateKey.Count must be 32");
            }

            // hack: abusing publicKey as temporary storage
            // todo: remove hack
            for (int i = 0; i < 32; i++)
            {
                publicKey[i] = privateKey[i];
            }
            ScalarOperations.sc_clamp(publicKey);

            GroupElementP3 A;

            GroupOperations.ge_scalarmult_base(out A, publicKey);
            EdwardsToMontgomeryX(out var publicKeyFE, ref A.Y, ref A.Z);
            FieldOperations.fe_tobytes(publicKey, in publicKeyFE);
        }
Beispiel #2
0
        public static void GetPublicKey(ArraySegment <byte> publicKey, ArraySegment <byte> privateKey)
        {
            if (publicKey.Array == null)
            {
                throw new ArgumentNullException("publicKey.Array");
            }
            if (privateKey.Array == null)
            {
                throw new ArgumentNullException("privateKey.Array");
            }
            if (publicKey.Count != PublicKeySizeInBytes)
            {
                throw new ArgumentException("privateKey.Count must be 32");
            }
            if (privateKey.Count != PrivateKeySizeInBytes)
            {
                throw new ArgumentException("privateKey.Count must be 32");
            }

            // hack: abusing publicKey as temporary storage
            // todo: remove hack
            for (int i = 0; i < 32; i++)
            {
                publicKey.Array[publicKey.Offset + i] = privateKey.Array[privateKey.Offset + i];
            }
            ScalarOperations.sc_clamp(publicKey.Array, publicKey.Offset);

            GroupElementP3 A;

            GroupOperations.ge_scalarmult_base(out A, publicKey.Array, publicKey.Offset);
            FieldElement publicKeyFE;

            EdwardsToMontgomeryX(out publicKeyFE, ref A.Y, ref A.Z);
            FieldOperations.fe_tobytes(publicKey.Array, publicKey.Offset, ref publicKeyFE);
        }
Beispiel #3
0
        public static void CryptoSign(
            byte[] sig, int sigoffset,
            byte[] m, int moffset, int mlen,
            byte[] sk, int skoffset)
        {
            var hasher = new Sha512();
            {
                hasher.Update(sk, skoffset, 32);
                var az = hasher.Finalize();
                ScalarOperations.Clamp(az, 0);

                hasher.Init();
                hasher.Update(az, 32, 32);
                hasher.Update(m, moffset, mlen);
                var r = hasher.Finalize();

                ScalarOperations.Reduce(r);
                GroupElementP3 R;
                GroupOperations.ScalarMultBase(out R, r, 0);
                GroupOperations.P3ToBytes(sig, sigoffset, ref R);

                hasher.Init();
                hasher.Update(sig, sigoffset, 32);
                hasher.Update(sk, skoffset + 32, 32);
                hasher.Update(m, moffset, mlen);
                var hram = hasher.Finalize();

                ScalarOperations.Reduce(hram);
                var s = new byte[32];
                Array.Copy(sig, sigoffset + 32, s, 0, 32);
                ScalarOperations.MulAdd(s, hram, az, r);
                Array.Copy(s, 0, sig, sigoffset + 32, 32);
                CryptoBytes.Wipe(s);
            }
        }
Beispiel #4
0
        public static void KeyExchange(Span <byte> sharedKey, ReadOnlySpan <byte> publicKey, ReadOnlySpan <byte> privateKey)
        {
            if (sharedKey.Length != 32)
            {
                throw new ArgumentException("sharedKey.Count != 32");
            }
            if (publicKey.Length != 32)
            {
                throw new ArgumentException("publicKey.Count != 32");
            }
            if (privateKey.Length != 64)
            {
                throw new ArgumentException("privateKey.Count != 64");
            }

            FieldOperations.fe_frombytes(out var edwardsY, publicKey);
            FieldOperations.fe_1(out var edwardsZ);
            MontgomeryCurve25519.EdwardsToMontgomeryX(out var montgomeryX, ref edwardsY, ref edwardsZ);
            Span <byte> h = stackalloc byte[64];

            Sha512.Hash(privateKey.Slice(0, 32), h);
            ScalarOperations.sc_clamp(h);
            MontgomeryOperations.scalarmult(out var sharedMontgomeryX, h, in montgomeryX);
            CryptoBytes.Wipe(h);
            FieldOperations.fe_tobytes(sharedKey, in sharedMontgomeryX);
        }
Beispiel #5
0
        public RingSignature[] Sign(byte[] msg, byte[] keyImage, IKey[] publicKeys, byte[] secretKey, int index)
        {
            RingSignature[] signatures = new RingSignature[publicKeys.Length];

            byte[][] pubs = publicKeys.Select(pk => pk.Value.ToArray()).ToArray();

            GroupOperations.ge_frombytes(out GroupElementP3 keyImageP3, keyImage, 0);
            GroupElementCached[] image_pre = new GroupElementCached[8];
            GroupOperations.ge_dsm_precomp(image_pre, ref keyImageP3);

            byte[] sum = new byte[32], k = null, h = null;

            IHash hasher = HashFactory.Crypto.SHA3.CreateKeccak256();

            hasher.TransformBytes(msg);

            for (int i = 0; i < publicKeys.Length; i++)
            {
                signatures[i] = new RingSignature();

                if (i == index)
                {
                    k = GetRandomSeed(true);
                    GroupOperations.ge_scalarmult_base(out GroupElementP3 tmp3, k, 0);
                    byte[] tmp3bytes = new byte[32];
                    GroupOperations.ge_p3_tobytes(tmp3bytes, 0, ref tmp3);
                    hasher.TransformBytes(tmp3bytes);
                    tmp3 = Hash2Point(pubs[i]);
                    GroupOperations.ge_scalarmult(out GroupElementP2 tmp2, k, ref tmp3);
                    byte[] tmp2bytes = new byte[32];
                    GroupOperations.ge_tobytes(tmp2bytes, 0, ref tmp2);
                    hasher.TransformBytes(tmp2bytes);
                }
                else
                {
                    signatures[i].C = GetRandomSeed(true);
                    signatures[i].R = GetRandomSeed(true);
                    GroupOperations.ge_frombytes(out GroupElementP3 tmp3, pubs[i], 0);
                    GroupOperations.ge_double_scalarmult_vartime(out GroupElementP2 tmp2, signatures[i].C, ref tmp3, signatures[i].R);
                    byte[] tmp2bytes = new byte[32];
                    GroupOperations.ge_tobytes(tmp2bytes, 0, ref tmp2);
                    hasher.TransformBytes(tmp2bytes);
                    tmp3 = Hash2Point(pubs[i]);
                    GroupOperations.ge_double_scalarmult_precomp_vartime(out tmp2, signatures[i].R, tmp3, signatures[i].C, image_pre);
                    tmp2bytes = new byte[32];
                    GroupOperations.ge_tobytes(tmp2bytes, 0, ref tmp2);
                    hasher.TransformBytes(tmp2bytes);
                    ScalarOperations.sc_add(sum, sum, signatures[i].C);
                }
            }

            h = hasher.TransformFinal().GetBytes();
            ScalarOperations.sc_sub(signatures[index].C, h, sum);
            ScalarOperations.sc_reduce32(signatures[index].C);
            ScalarOperations.sc_mulsub(signatures[index].R, signatures[index].C, secretKey, k);
            ScalarOperations.sc_reduce32(signatures[index].R);

            return(signatures);
        }
Beispiel #6
0
 private static GroupElementP3 Hash2Point(byte[] hashed)
 {
     byte[] hashValue = HashFactory.Crypto.SHA3.CreateKeccak256().ComputeBytes(hashed).GetBytes();
     //byte[] hashValue = HashFactory.Crypto.SHA3.CreateKeccak512().ComputeBytes(hashed).GetBytes();
     ScalarOperations.sc_reduce32(hashValue);
     GroupOperations.ge_fromfe_frombytes_vartime(out GroupElementP2 p2, hashValue, 0);
     GroupOperations.ge_mul8(out GroupElementP1P1 p1p1, ref p2);
     GroupOperations.ge_p1p1_to_p3(out GroupElementP3 p3, ref p1p1);
     return(p3);
 }
Beispiel #7
0
        public static void KeyExchange(ArraySegment <byte> sharedKey, ArraySegment <byte> publicKey, ArraySegment <byte> privateKey, bool naclCompat = false)
        {
            if (sharedKey.Array == null)
            {
                throw new ArgumentNullException("sharedKey.Array");
            }
            if (publicKey.Array == null)
            {
                throw new ArgumentNullException("publicKey.Array");
            }
            if (privateKey.Array == null)
            {
                throw new ArgumentNullException("privateKey");
            }
            if (sharedKey.Count != SharedKeySizeInBytes)
            {
                throw new ArgumentException("sharedKey.Count != 32");
            }
            if (publicKey.Count != PublicKeySizeInBytes)
            {
                throw new ArgumentException("publicKey.Count != 32");
            }
            if (privateKey.Count != ExpandedPrivateKeySizeInBytes)
            {
                throw new ArgumentException("privateKey.Count != 64");
            }

            FieldElement montgomeryX, edwardsY, edwardsZ, sharedMontgomeryX;

            FieldOperations.fe_frombytes(out edwardsY, publicKey.Array, publicKey.Offset);
            FieldOperations.fe_1(out edwardsZ);
            Curve25519.EdwardsToMontgomeryX(out montgomeryX, ref edwardsY, ref edwardsZ);

            IHash hasher = AuthenticatorFactory.CreateHashPrimitive(HashFunction.Sha512);

            hasher.BlockUpdate(privateKey.Array, privateKey.Offset, 32);
            byte[] h = new byte[64];
            hasher.DoFinal(h, 0);
            ScalarOperations.sc_clamp(h, 0);
            MontgomeryOperations.scalarmult(out sharedMontgomeryX, h, 0, ref montgomeryX);
            h.SecureWipe();
            FieldOperations.fe_tobytes(sharedKey.Array, sharedKey.Offset, ref sharedMontgomeryX);

            if (naclCompat)
            {
                Curve25519.KeyExchangeOutputHashNaCl(sharedKey.Array, sharedKey.Offset);
            }
        }
        public static void KeyExchange(ArraySegment <byte> sharedKey, ArraySegment <byte> publicKey, ArraySegment <byte> privateKey)
        {
            if (sharedKey.Array == null)
            {
                throw new ArgumentNullException("sharedKey.Array");
            }
            if (publicKey.Array == null)
            {
                throw new ArgumentNullException("publicKey.Array");
            }
            if (privateKey.Array == null)
            {
                throw new ArgumentNullException("privateKey");
            }
            if (sharedKey.Count != 32)
            {
                throw new ArgumentException("sharedKey.Count != 32");
            }
            if (publicKey.Count != 32)
            {
                throw new ArgumentException("publicKey.Count != 32");
            }
            if (privateKey.Count != 64)
            {
                throw new ArgumentException("privateKey.Count != 64");
            }

            FieldElement montgomeryX, edwardsY, edwardsZ, sharedMontgomeryX;

            FieldOperations.fe_frombytes(out edwardsY, publicKey.Array, publicKey.Offset);
            FieldOperations.fe_1(out edwardsZ);
            MontgomeryCurve25519.EdwardsToMontgomeryX(out montgomeryX, ref edwardsY, ref edwardsZ);
            var blake2bConfig = new Blake2BConfig
            {
                OutputSizeInBytes = 64
            };
            var hasher = Blake2B.Create(blake2bConfig);

            hasher.Update(privateKey.Array, privateKey.Offset, 32);
            byte[] h = hasher.Finish();
            //byte[] h = Sha512.Hash(privateKey.Array, privateKey.Offset, 32);//ToDo: Remove alloc
            ScalarOperations.sc_clamp(h, 0);
            MontgomeryOperations.scalarmult(out sharedMontgomeryX, h, 0, ref montgomeryX);
            CryptoBytes.Wipe(h);
            FieldOperations.fe_tobytes(sharedKey.Array, sharedKey.Offset, ref sharedMontgomeryX);
            MontgomeryCurve25519.KeyExchangeOutputHashNaCl(sharedKey.Array, sharedKey.Offset);
        }
Beispiel #9
0
        internal static void crypto_sign2(
            byte[] sig,
            byte[] m,
            byte[] sk,
            int keylen)
        {
            byte[]         privHash   = new byte[64];
            byte[]         seededHash = new byte[64];
            byte[]         result     = new byte[64];
            GroupElementP3 R          = new GroupElementP3();
            var            hasher     = new KeccakDigest(512);
            {
                var reversedPrivateKey = new byte[keylen];
                Array.Copy(sk, 0, reversedPrivateKey, 0, keylen);
                Array.Reverse(reversedPrivateKey);

                hasher.BlockUpdate(reversedPrivateKey, 0, keylen);
                hasher.DoFinal(privHash, 0);

                ScalarOperations.sc_clamp(privHash, 0);

                hasher.Reset();
                hasher.BlockUpdate(privHash, 32, 32);
                hasher.BlockUpdate(m, 0, m.Length);
                hasher.DoFinal(seededHash, 0);

                ScalarOperations.sc_reduce(seededHash);

                GroupOperations.ge_scalarmult_base(out R, seededHash, 0);
                GroupOperations.ge_p3_tobytes(sig, 0, ref R);

                hasher.Reset();
                hasher.BlockUpdate(sig, 0, 32);
                hasher.BlockUpdate(sk, keylen, 32);
                hasher.BlockUpdate(m, 0, m.Length);
                hasher.DoFinal(result, 0);

                ScalarOperations.sc_reduce(result);

                var s = new byte[32]; //todo: remove allocation
                Array.Copy(sig, 32, s, 0, 32);
                ScalarOperations.sc_muladd(s, result, privHash, seededHash);
                Array.Copy(s, 0, sig, 32, 32);

                CryptoBytes.Wipe(s);
            }
        }
Beispiel #10
0
        public static void key_derive(byte[] shared, byte[] salt, byte[] secretKey, byte[] pubkey)
        {
            var longKeyHash  = new byte[64];
            var shortKeyHash = new byte[32];

            Array.Reverse(secretKey);

            // compute  Sha3(512) hash of secret key (as in prepareForScalarMultiply)
            var digestSha3 = new KeccakDigest(512);

            digestSha3.BlockUpdate(secretKey, 0, 32);
            digestSha3.DoFinal(longKeyHash, 0);

            longKeyHash[0]  &= 248;
            longKeyHash[31] &= 127;
            longKeyHash[31] |= 64;

            Array.Copy(longKeyHash, 0, shortKeyHash, 0, 32);

            ScalarOperations.sc_clamp(shortKeyHash, 0);

            var p = new[] { new long[16], new long[16], new long[16], new long[16] };
            var q = new[] { new long[16], new long[16], new long[16], new long[16] };

            TweetNaCl.TweetNaCl.Unpackneg(q, pubkey); // returning -1 invalid signature

            TweetNaCl.TweetNaCl.Scalarmult(p, q, shortKeyHash, 0);

            TweetNaCl.TweetNaCl.Pack(shared, p);

            // for some reason the most significant bit of the last byte needs to be flipped.
            // doesnt seem to be any corrosponding action in nano/nem.core, so this may be an issue in one of the above 3 functions. i have no idea.
            shared[31] ^= (1 << 7);

            // salt
            for (var i = 0; i < salt.Length; i++)
            {
                shared[i] ^= salt[i];
            }

            // hash salted shared key
            var digestSha3Two = new KeccakDigest(256);

            digestSha3Two.BlockUpdate(shared, 0, 32);
            digestSha3Two.DoFinal(shared, 0);
        }
Beispiel #11
0
		/// <summary>
		/// Calculates a new public key by processing an existing one with this license bock.
		/// The key is calculated as following: <code>new_pub_key = pub_key * hash + parent</code>.
		/// Where <code>pub_key</code> and <code>parent</code> are public keys, and <code>hash</code> a private key.
		/// </summary>
		/// <param name="parent">The preceeding key (from the previous block or root key).</param>
		/// <returns>The new public key after processing it with this block.</returns>
		public byte[] DeriveKey(ReadOnlySpan<byte> parent)
		{
			ScalarOperations.sc_clamp(Hash);
			GroupOperations.ge_frombytes_negate_vartime(out var pubkey, Key);
			GroupOperations.ge_frombytes_negate_vartime(out var parkey, parent);

			GroupOperations.ge_scalarmult_vartime(out GroupElementP1P1 res, Hash, pubkey);
			GroupOperations.ge_p3_to_cached(out var pargrp, parkey);

			GroupOperations.ge_p1p1_to_p3(out var r, res);
			GroupOperations.ge_add(out var a, r, pargrp);
			GroupOperations.ge_p1p1_to_p3(out var r2, a);
			var final = new byte[32];
			GroupOperations.ge_p3_tobytes(final, r2);
			final[31] ^= 0x80;

			return final;
		}
        public static void crypto_sign_keypair(byte[] pk, int pkoffset, byte[] sk, int skoffset, byte[] seed, int seedoffset)
        {
            GroupElementP3 A;
            int            i;

            Array.Copy(seed, seedoffset, sk, skoffset, 32);
            byte[] h = SHA512.Hash(sk, skoffset, 32);//ToDo: Remove alloc
            ScalarOperations.sc_clamp(h, 0);

            GroupOperations.ge_scalarmult_base(out A, h, 0);
            GroupOperations.ge_p3_tobytes(pk, pkoffset, ref A);

            for (i = 0; i < 32; ++i)
            {
                sk[skoffset + 32 + i] = pk[pkoffset + i];
            }

            CryptoExtensions.Wipe(h);
        }
Beispiel #13
0
        public static void KeyExchange(ArraySegment <byte> sharedKey, ArraySegment <byte> publicKey,
                                       ArraySegment <byte> privateKey)
        {
            if (sharedKey.Array == null)
            {
                throw new ArgumentNullException(nameof(sharedKey));
            }

            if (publicKey.Array == null)
            {
                throw new ArgumentNullException(nameof(sharedKey));
            }

            if (privateKey.Array == null)
            {
                throw new ArgumentNullException("privateKey");
            }

            if (sharedKey.Count != 32)
            {
                throw new ArgumentException("sharedKey.Count != 32");
            }

            if (publicKey.Count != 32)
            {
                throw new ArgumentException("publicKey.Count != 32");
            }

            if (privateKey.Count != 64)
            {
                throw new ArgumentException("privateKey.Count != 64");
            }

            FieldOperations.fe_frombytes(out FieldElement edwardsY, publicKey.Array, publicKey.Offset);
            FieldOperations.fe_1(out FieldElement edwardsZ);
            MontgomeryCurve25519.EdwardsToMontgomeryX(out FieldElement montgomeryX, ref edwardsY, ref edwardsZ);
            byte[] h = Sha512.Hash(privateKey.Array, privateKey.Offset, 32);
            ScalarOperations.ScClamp(h, 0);
            MontgomeryOperations.ScalarMult(out FieldElement sharedMontgomeryX, h, 0, ref montgomeryX);
            CryptoBytes.Wipe(h);
            FieldOperations.fe_tobytes(sharedKey.Array, sharedKey.Offset, ref sharedMontgomeryX);
            MontgomeryCurve25519.KeyExchangeOutputHashNaCl(sharedKey.Array, sharedKey.Offset);
        }
Beispiel #14
0
        public static void CryptoSignKeyPair(byte[] pk, int pkoffset, byte[] sk, int skoffset, byte[] seed,
                                             int seedoffset)
        {
            int i;

            Array.Copy(seed, seedoffset, sk, skoffset, 32);
            var h = Sha512.Hash(sk, skoffset, 32);

            ScalarOperations.Clamp(h, 0);

            GroupOperations.ScalarMultBase(out var A, h, 0);
            GroupOperations.P3ToBytes(pk, pkoffset, ref A);

            for (i = 0; i < 32; ++i)
            {
                sk[skoffset + 32 + i] = pk[pkoffset + i];
            }
            CryptoBytes.Wipe(h);
        }
Beispiel #15
0
        public static void KeyExchange(ArraySegment <byte> sharedKey, ArraySegment <byte> publicKey, ArraySegment <byte> privateKey)
        {
            if (sharedKey.Array == null)
            {
                throw new ArgumentNullException("sharedKey.Array");
            }
            if (publicKey.Array == null)
            {
                throw new ArgumentNullException("publicKey.Array");
            }
            if (privateKey.Array == null)
            {
                throw new ArgumentNullException("privateKey");
            }
            if (sharedKey.Count != 32)
            {
                throw new ArgumentException("sharedKey.Count != 32");
            }
            if (publicKey.Count != 32)
            {
                throw new ArgumentException("publicKey.Count != 32");
            }
            if (privateKey.Count != 64)
            {
                throw new ArgumentException("privateKey.Count != 64");
            }

            FieldElement montgomeryX, edwardsY, edwardsZ, sharedMontgomeryX;

            FieldOperations.fe_frombytes(out edwardsY, publicKey.Array, publicKey.Offset);
            FieldOperations.fe_1(out edwardsZ);
            MontgomeryCurve25519.EdwardsToMontgomeryX(out montgomeryX, ref edwardsY, ref edwardsZ);
            var hasher = Blake2Fast.Blake2b.CreateIncrementalHasher(64);

            hasher.Update(new ArraySegment <byte>(privateKey.Array, privateKey.Offset, 32));
            byte[] h = hasher.Finish();
            ScalarOperations.sc_clamp(h, 0);
            MontgomeryOperations.scalarmult(out sharedMontgomeryX, h, 0, ref montgomeryX);
            CryptoBytes.Wipe(h);
            FieldOperations.fe_tobytes(sharedKey.Array, sharedKey.Offset, ref sharedMontgomeryX);
            MontgomeryCurve25519.KeyExchangeOutputHashNaCl(sharedKey.Array, sharedKey.Offset);
        }
Beispiel #16
0
        public bool Verify(byte[] msg, byte[] keyImage, IKey[] publicKeys, RingSignature[] signatures)
        {
            byte[][] pubs = publicKeys.Select(pk => pk.Value.ToArray()).ToArray();
            GroupOperations.ge_frombytes(out GroupElementP3 image_unp, keyImage, 0);

            GroupElementCached[] image_pre = new GroupElementCached[8];
            GroupOperations.ge_dsm_precomp(image_pre, ref image_unp);
            byte[] sum = new byte[32];

            IHash hasher = HashFactory.Crypto.SHA3.CreateKeccak256();

            hasher.TransformBytes(msg);

            for (int i = 0; i < pubs.Length; i++)
            {
                if (ScalarOperations.sc_check(signatures[i].C) != 0 || ScalarOperations.sc_check(signatures[i].R) != 0)
                {
                    return(false);
                }

                GroupOperations.ge_frombytes(out GroupElementP3 tmp3, pubs[i], 0);
                GroupOperations.ge_double_scalarmult_vartime(out GroupElementP2 tmp2, signatures[i].C, ref tmp3, signatures[i].R);
                byte[] tmp2bytes = new byte[32];
                GroupOperations.ge_tobytes(tmp2bytes, 0, ref tmp2);
                hasher.TransformBytes(tmp2bytes);
                tmp3 = Hash2Point(pubs[i]);
                GroupOperations.ge_double_scalarmult_precomp_vartime(out tmp2, signatures[i].R, tmp3, signatures[i].C, image_pre);
                tmp2bytes = new byte[32];
                GroupOperations.ge_tobytes(tmp2bytes, 0, ref tmp2);
                hasher.TransformBytes(tmp2bytes);
                ScalarOperations.sc_add(sum, sum, signatures[i].C);
            }

            byte[] h = hasher.TransformFinal().GetBytes();
            ScalarOperations.sc_reduce32(h);
            ScalarOperations.sc_sub(h, h, sum);

            int res = ScalarOperations.sc_isnonzero(h);

            return(res == 0);
        }
Beispiel #17
0
        //Needs more testing
        public static void KeyExchange(ArraySegment <byte> sharedKey, ArraySegment <byte> publicKey, ArraySegment <byte> privateKey)
        {
            Throw.If(sharedKey.Array == null, "sharedKey.Array");
            Throw.If(publicKey.Array == null, "publicKey.Array");
            Throw.If(privateKey.Array == null, "privateKey");
            Throw.If(sharedKey.Count != 32, "sharedKey.Count != 32");
            Throw.If(publicKey.Count != 32, "publicKey.Count != 32");
            Throw.If(privateKey.Count != 64, "privateKey.Count != 64");

            FieldElement montgomeryX, edwardsY, edwardsZ, sharedMontgomeryX;

            FieldOperations.fe_frombytes(out edwardsY, publicKey.Array, publicKey.Offset);
            FieldOperations.fe_1(out edwardsZ);
            EdwardsToMontgomeryX(out montgomeryX, ref edwardsY, ref edwardsZ);
            byte[] h = SHA512.Hash(privateKey.Array, privateKey.Offset, 32);//ToDo: Remove alloc
            ScalarOperations.sc_clamp(h, 0);
            MontgomeryOperations.scalarmult(out sharedMontgomeryX, h, 0, ref montgomeryX);
            CryptoExtensions.Wipe(h);
            FieldOperations.fe_tobytes(sharedKey.Array, sharedKey.Offset, ref sharedMontgomeryX);
            KeyExchangeOutputHashNaCl(sharedKey.Array, sharedKey.Offset);
        }
        public static bool crypto_sign_verify(
            byte[] sig, int sigoffset,
            byte[] m, int moffset, int mlen,
            byte[] pk, int pkoffset)
        {
            byte[]         h;
            byte[]         checkr = new byte[32];
            GroupElementP3 A;
            GroupElementP2 R;

            if ((sig[sigoffset + 63] & 224) != 0)
            {
                return(false);
            }
            if (GroupOperations.ge_frombytes_negate_vartime(out A, pk, pkoffset) != 0)
            {
                return(false);
            }

            var hasher = new Sha512();

            hasher.Update(sig, sigoffset, 32);
            hasher.Update(pk, pkoffset, 32);
            hasher.Update(m, moffset, mlen);
            h = hasher.Finalize();

            ScalarOperations.sc_reduce(h);

            var sm32 = new byte[32];

            Array.Copy(sig, sigoffset + 32, sm32, 0, 32);
            GroupOperations.ge_double_scalarmult_vartime(out R, h, ref A, sm32);
            GroupOperations.ge_tobytes(checkr, 0, ref R);
            var result = CryptoBytes.ConstantTimeEquals(checkr, 0, sig, sigoffset, 32);

            CryptoBytes.Wipe(h);
            CryptoBytes.Wipe(checkr);
            return(result);
        }
Beispiel #19
0
        public static bool CryptoSignVerify(
            byte[] sig, int sigoffset,
            byte[] m, int moffset, int mlen,
            byte[] pk, int pkoffset)
        {
            var            checker = new byte[32];
            GroupElementP3 A;
            GroupElementP2 R;

            if ((sig[sigoffset + 63] & 224) != 0)
            {
                return(false);
            }
            if (GroupOperations.FromBytes(out A, pk, pkoffset) != 0)
            {
                return(false);
            }

            var hasher = new Sha512();

            hasher.Update(sig, sigoffset, 32);
            hasher.Update(pk, pkoffset, 32);
            hasher.Update(m, moffset, mlen);
            var h = hasher.Finalize();

            ScalarOperations.Reduce(h);

            var sm32 = new byte[32];

            Array.Copy(sig, sigoffset + 32, sm32, 0, 32);
            GroupOperations.DoubleScalarMult(out R, h, ref A, sm32);
            GroupOperations.ToBytes(checker, 0, ref R);
            var result = CryptoBytes.ConstantTimeEquals(checker, 0, sig, sigoffset, 32);

            CryptoBytes.Wipe(h);
            CryptoBytes.Wipe(checker);
            return(result);
        }
        public static void GenerateSignature(
            byte[] sig, int sigoffset,
            byte[] m, int moffset, int mlen,
            byte[] sk, int skoffset)
        {
            byte[]         az;
            byte[]         r;
            byte[]         hram;
            GroupElementP3 R;
            var            hasher = new SHA512();
            {
                hasher.Update(sk, skoffset, 32);
                az = hasher.Finish();
                ScalarOperations.sc_clamp(az, 0);

                hasher.Init();
                hasher.Update(az, 32, 32);
                hasher.Update(m, moffset, mlen);
                r = hasher.Finish();

                ScalarOperations.sc_reduce(r);
                GroupOperations.ge_scalarmult_base(out R, r, 0);
                GroupOperations.ge_p3_tobytes(sig, sigoffset, ref R);

                hasher.Init();
                hasher.Update(sig, sigoffset, 32);
                hasher.Update(sk, skoffset + 32, 32);
                hasher.Update(m, moffset, mlen);
                hram = hasher.Finish();

                ScalarOperations.sc_reduce(hram);
                var s = new byte[32];//todo: remove allocation
                Array.Copy(sig, sigoffset + 32, s, 0, 32);
                ScalarOperations.sc_muladd(s, hram, az, r);
                Array.Copy(s, 0, sig, sigoffset + 32, 32);
                CryptoExtensions.Wipe(s);
            }
        }
Beispiel #21
0
        private static byte[] GetRandomSeed(bool reduced = false)
        {
            byte[] seed = new byte[32];
            if (!reduced)
            {
                RNGCryptoServiceProvider.Create().GetNonZeroBytes(seed);
            }
            else
            {
                byte[] limit = { 0xe3, 0x6a, 0x67, 0x72, 0x8b, 0xce, 0x13, 0x29, 0x8f, 0x30, 0x82, 0x8c, 0x0b, 0xa4, 0x10, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0 };
                bool   isZero = false, less32 = false;
                do
                {
                    RNGCryptoServiceProvider.Create().GetNonZeroBytes(seed);
                    isZero = ScalarOperations.sc_isnonzero(seed) == 0;
                    less32 = Less32(seed, limit);
                } while (isZero && !less32);

                ScalarOperations.sc_reduce32(seed);
            }

            return(seed);
        }
Beispiel #22
0
        internal static void scalarmult(
            out FieldElement q,
            byte[] n, int noffset,
            ref FieldElement p)
        {
            byte[]       e = new byte[32];      //ToDo: remove allocation
            UInt32       i;
            FieldElement x1;
            FieldElement x2;
            FieldElement z2;
            FieldElement x3;
            FieldElement z3;
            FieldElement tmp0;
            FieldElement tmp1;
            int          pos;
            UInt32       swap;
            UInt32       b;

            for (i = 0; i < 32; ++i)
            {
                e[i] = n[noffset + i];
            }
            ScalarOperations.sc_clamp(e, 0);
            x1 = p;
            FieldOperations.fe_1(out x2);
            FieldOperations.fe_0(out z2);
            x3 = x1;
            FieldOperations.fe_1(out z3);

            swap = 0;
            for (pos = 254; pos >= 0; --pos)
            {
                b     = (uint)(e[pos / 8] >> (pos & 7));
                b    &= 1;
                swap ^= b;
                FieldOperations.fe_cswap(ref x2, ref x3, swap);
                FieldOperations.fe_cswap(ref z2, ref z3, swap);
                swap = b;
                /* qhasm: fe X2 */

                /* qhasm: fe Z2 */

                /* qhasm: fe X3 */

                /* qhasm: fe Z3 */

                /* qhasm: fe X4 */

                /* qhasm: fe Z4 */

                /* qhasm: fe X5 */

                /* qhasm: fe Z5 */

                /* qhasm: fe A */

                /* qhasm: fe B */

                /* qhasm: fe C */

                /* qhasm: fe D */

                /* qhasm: fe E */

                /* qhasm: fe AA */

                /* qhasm: fe BB */

                /* qhasm: fe DA */

                /* qhasm: fe CB */

                /* qhasm: fe t0 */

                /* qhasm: fe t1 */

                /* qhasm: fe t2 */

                /* qhasm: fe t3 */

                /* qhasm: fe t4 */

                /* qhasm: enter ladder */

                /* qhasm: D = X3-Z3 */
                /* asm 1: fe_sub(>D=fe#5,<X3=fe#3,<Z3=fe#4); */
                /* asm 2: fe_sub(>D=tmp0,<X3=x3,<Z3=z3); */
                FieldOperations.fe_sub(out tmp0, ref x3, ref z3);

                /* qhasm: B = X2-Z2 */
                /* asm 1: fe_sub(>B=fe#6,<X2=fe#1,<Z2=fe#2); */
                /* asm 2: fe_sub(>B=tmp1,<X2=x2,<Z2=z2); */
                FieldOperations.fe_sub(out tmp1, ref x2, ref z2);

                /* qhasm: A = X2+Z2 */
                /* asm 1: fe_add(>A=fe#1,<X2=fe#1,<Z2=fe#2); */
                /* asm 2: fe_add(>A=x2,<X2=x2,<Z2=z2); */
                FieldOperations.fe_add(out x2, ref x2, ref z2);

                /* qhasm: C = X3+Z3 */
                /* asm 1: fe_add(>C=fe#2,<X3=fe#3,<Z3=fe#4); */
                /* asm 2: fe_add(>C=z2,<X3=x3,<Z3=z3); */
                FieldOperations.fe_add(out z2, ref x3, ref z3);

                /* qhasm: DA = D*A */
                /* asm 1: fe_mul(>DA=fe#4,<D=fe#5,<A=fe#1); */
                /* asm 2: fe_mul(>DA=z3,<D=tmp0,<A=x2); */
                FieldOperations.fe_mul(out z3, ref tmp0, ref x2);

                /* qhasm: CB = C*B */
                /* asm 1: fe_mul(>CB=fe#2,<C=fe#2,<B=fe#6); */
                /* asm 2: fe_mul(>CB=z2,<C=z2,<B=tmp1); */
                FieldOperations.fe_mul(out z2, ref z2, ref tmp1);

                /* qhasm: BB = B^2 */
                /* asm 1: fe_sq(>BB=fe#5,<B=fe#6); */
                /* asm 2: fe_sq(>BB=tmp0,<B=tmp1); */
                FieldOperations.fe_sq(out tmp0, ref tmp1);

                /* qhasm: AA = A^2 */
                /* asm 1: fe_sq(>AA=fe#6,<A=fe#1); */
                /* asm 2: fe_sq(>AA=tmp1,<A=x2); */
                FieldOperations.fe_sq(out tmp1, ref x2);

                /* qhasm: t0 = DA+CB */
                /* asm 1: fe_add(>t0=fe#3,<DA=fe#4,<CB=fe#2); */
                /* asm 2: fe_add(>t0=x3,<DA=z3,<CB=z2); */
                FieldOperations.fe_add(out x3, ref z3, ref z2);

                /* qhasm: assign x3 to t0 */

                /* qhasm: t1 = DA-CB */
                /* asm 1: fe_sub(>t1=fe#2,<DA=fe#4,<CB=fe#2); */
                /* asm 2: fe_sub(>t1=z2,<DA=z3,<CB=z2); */
                FieldOperations.fe_sub(out z2, ref z3, ref z2);

                /* qhasm: X4 = AA*BB */
                /* asm 1: fe_mul(>X4=fe#1,<AA=fe#6,<BB=fe#5); */
                /* asm 2: fe_mul(>X4=x2,<AA=tmp1,<BB=tmp0); */
                FieldOperations.fe_mul(out x2, ref tmp1, ref tmp0);

                /* qhasm: E = AA-BB */
                /* asm 1: fe_sub(>E=fe#6,<AA=fe#6,<BB=fe#5); */
                /* asm 2: fe_sub(>E=tmp1,<AA=tmp1,<BB=tmp0); */
                FieldOperations.fe_sub(out tmp1, ref tmp1, ref tmp0);

                /* qhasm: t2 = t1^2 */
                /* asm 1: fe_sq(>t2=fe#2,<t1=fe#2); */
                /* asm 2: fe_sq(>t2=z2,<t1=z2); */
                FieldOperations.fe_sq(out z2, ref z2);

                /* qhasm: t3 = a24*E */
                /* asm 1: fe_mul121666(>t3=fe#4,<E=fe#6); */
                /* asm 2: fe_mul121666(>t3=z3,<E=tmp1); */
                FieldOperations.fe_mul121666(out z3, ref tmp1);

                /* qhasm: X5 = t0^2 */
                /* asm 1: fe_sq(>X5=fe#3,<t0=fe#3); */
                /* asm 2: fe_sq(>X5=x3,<t0=x3); */
                FieldOperations.fe_sq(out x3, ref x3);

                /* qhasm: t4 = BB+t3 */
                /* asm 1: fe_add(>t4=fe#5,<BB=fe#5,<t3=fe#4); */
                /* asm 2: fe_add(>t4=tmp0,<BB=tmp0,<t3=z3); */
                FieldOperations.fe_add(out tmp0, ref tmp0, ref z3);

                /* qhasm: Z5 = X1*t2 */
                /* asm 1: fe_mul(>Z5=fe#4,x1,<t2=fe#2); */
                /* asm 2: fe_mul(>Z5=z3,x1,<t2=z2); */
                FieldOperations.fe_mul(out z3, ref x1, ref z2);

                /* qhasm: Z4 = E*t4 */
                /* asm 1: fe_mul(>Z4=fe#2,<E=fe#6,<t4=fe#5); */
                /* asm 2: fe_mul(>Z4=z2,<E=tmp1,<t4=tmp0); */
                FieldOperations.fe_mul(out z2, ref tmp1, ref tmp0);

                /* qhasm: return */
            }
            FieldOperations.fe_cswap(ref x2, ref x3, swap);
            FieldOperations.fe_cswap(ref z2, ref z3, swap);

            FieldOperations.fe_invert(out z2, ref z2);
            FieldOperations.fe_mul(out x2, ref x2, ref z2);
            q = x2;
            CryptoExtensions.Wipe(e);
        }
Beispiel #23
0
        private static void ScalarMult(
            out FieldElement q,
            byte[] n, int noffset,
            ref FieldElement p)
        {
            var          e = new byte[32]; //ToDo: remove allocation
            FieldElement tmp1;

            for (var i = 0; i < 32; ++i)
            {
                e[i] = n[noffset + i];
            }
            ScalarOperations.Clamp(e, 0);
            var x1 = p;

            FieldOperations.FieldOperations_1(out var x2);
            FieldOperations.FieldOperations_0(out var z2);
            var x3 = x1;

            FieldOperations.FieldOperations_1(out var z3);

            uint swap = 0;

            for (var pos = 254; pos >= 0; --pos)
            {
                var b = (uint)(e[pos / 8] >> (pos & 7));
                b    &= 1;
                swap ^= b;
                FieldOperations.ControlledSwap(ref x2, ref x3, swap);
                FieldOperations.ControlledSwap(ref z2, ref z3, swap);
                swap = b;

                /* qhasm: enter ladder */

                /* qhasm: D = X3-Z3 */
                /* asm 1: Subtract(>D=fe#5,<X3=fe#3,<Z3=fe#4); */
                /* asm 2: Subtract(>D=tmp0,<X3=x3,<Z3=z3); */
                FieldOperations.Subtract(out var tmp0, ref x3, ref z3);

                /* qhasm: B = X2-Z2 */
                /* asm 1: Subtract(>B=fe#6,<X2=fe#1,<Z2=fe#2); */
                /* asm 2: Subtract(>B=tmp1,<X2=x2,<Z2=z2); */
                FieldOperations.Subtract(out tmp1, ref x2, ref z2);

                /* qhasm: A = X2+Z2 */
                /* asm 1: Add(>A=fe#1,<X2=fe#1,<Z2=fe#2); */
                /* asm 2: Add(>A=x2,<X2=x2,<Z2=z2); */
                FieldOperations.Add(out x2, ref x2, ref z2);

                /* qhasm: C = X3+Z3 */
                /* asm 1: Add(>C=fe#2,<X3=fe#3,<Z3=fe#4); */
                /* asm 2: Add(>C=z2,<X3=x3,<Z3=z3); */
                FieldOperations.Add(out z2, ref x3, ref z3);

                /* qhasm: DA = D*A */
                /* asm 1: Multiplication(>DA=fe#4,<D=fe#5,<A=fe#1); */
                /* asm 2: Multiplication(>DA=z3,<D=tmp0,<A=x2); */
                FieldOperations.Multiplication(out z3, ref tmp0, ref x2);

                /* qhasm: CB = C*B */
                /* asm 1: Multiplication(>CB=fe#2,<C=fe#2,<B=fe#6); */
                /* asm 2: Multiplication(>CB=z2,<C=z2,<B=tmp1); */
                FieldOperations.Multiplication(out z2, ref z2, ref tmp1);

                /* qhasm: BB = B^2 */
                /* asm 1: Square(>BB=fe#5,<B=fe#6); */
                /* asm 2: Square(>BB=tmp0,<B=tmp1); */
                FieldOperations.Square(out tmp0, ref tmp1);

                /* qhasm: AA = A^2 */
                /* asm 1: Square(>AA=fe#6,<A=fe#1); */
                /* asm 2: Square(>AA=tmp1,<A=x2); */
                FieldOperations.Square(out tmp1, ref x2);

                /* qhasm: t0 = DA+CB */
                /* asm 1: Add(>t0=fe#3,<DA=fe#4,<CB=fe#2); */
                /* asm 2: Add(>t0=x3,<DA=z3,<CB=z2); */
                FieldOperations.Add(out x3, ref z3, ref z2);

                /* qhasm: assign x3 to t0 */

                /* qhasm: t1 = DA-CB */
                /* asm 1: Subtract(>t1=fe#2,<DA=fe#4,<CB=fe#2); */
                /* asm 2: Subtract(>t1=z2,<DA=z3,<CB=z2); */
                FieldOperations.Subtract(out z2, ref z3, ref z2);

                /* qhasm: X4 = AA*BB */
                /* asm 1: Multiplication(>X4=fe#1,<AA=fe#6,<BB=fe#5); */
                /* asm 2: Multiplication(>X4=x2,<AA=tmp1,<BB=tmp0); */
                FieldOperations.Multiplication(out x2, ref tmp1, ref tmp0);

                /* qhasm: E = AA-BB */
                /* asm 1: Subtract(>E=fe#6,<AA=fe#6,<BB=fe#5); */
                /* asm 2: Subtract(>E=tmp1,<AA=tmp1,<BB=tmp0); */
                FieldOperations.Subtract(out tmp1, ref tmp1, ref tmp0);

                /* qhasm: t2 = t1^2 */
                /* asm 1: Square(>t2=fe#2,<t1=fe#2); */
                /* asm 2: Square(>t2=z2,<t1=z2); */
                FieldOperations.Square(out z2, ref z2);

                /* qhasm: t3 = a24*E */
                /* asm 1: Multiplication121666(>t3=fe#4,<E=fe#6); */
                /* asm 2: Multiplication121666(>t3=z3,<E=tmp1); */
                FieldOperations.Multiplication121666(out z3, ref tmp1);

                /* qhasm: X5 = t0^2 */
                /* asm 1: Square(>X5=fe#3,<t0=fe#3); */
                /* asm 2: Square(>X5=x3,<t0=x3); */
                FieldOperations.Square(out x3, ref x3);

                /* qhasm: t4 = BB+t3 */
                /* asm 1: Add(>t4=fe#5,<BB=fe#5,<t3=fe#4); */
                /* asm 2: Add(>t4=tmp0,<BB=tmp0,<t3=z3); */
                FieldOperations.Add(out tmp0, ref tmp0, ref z3);

                /* qhasm: Z5 = X1*t2 */
                /* asm 1: Multiplication(>Z5=fe#4,x1,<t2=fe#2); */
                /* asm 2: Multiplication(>Z5=z3,x1,<t2=z2); */
                FieldOperations.Multiplication(out z3, ref x1, ref z2);

                /* qhasm: Z4 = E*t4 */
                /* asm 1: Multiplication(>Z4=fe#2,<E=fe#6,<t4=fe#5); */
                /* asm 2: Multiplication(>Z4=z2,<E=tmp1,<t4=tmp0); */
                FieldOperations.Multiplication(out z2, ref tmp1, ref tmp0);

                /* qhasm: return */
            }

            FieldOperations.ControlledSwap(ref x2, ref x3, swap);
            FieldOperations.ControlledSwap(ref z2, ref z3, swap);

            FieldOperations.Invert(out z2, ref z2);
            FieldOperations.Multiplication(out x2, ref x2, ref z2);
            q = x2;
            CryptoBytes.Wipe(e);
        }
Beispiel #24
0
        public byte[] CreateResponse(string password, ReadOnlySpan <byte> authenticationData)
        {
            // Java reference: https://github.com/MariaDB/mariadb-connector-j/blob/master/src/main/java/org/mariadb/jdbc/internal/com/send/authentication/Ed25519PasswordPlugin.java
            // C reference:  https://github.com/MariaDB/server/blob/592fe954ef82be1bc08b29a8e54f7729eb1e1343/plugin/auth_ed25519/ref10/sign.c#L7

            /*** Java
             *      byte[] bytePwd;
             *      if (passwordCharacterEncoding != null && !passwordCharacterEncoding.isEmpty()) {
             *              bytePwd = password.getBytes(passwordCharacterEncoding);
             *      } else {
             *              bytePwd = password.getBytes();
             *      }
             */
            byte[] passwordBytes = Encoding.UTF8.GetBytes(password);

            /*** Java
             *      MessageDigest hash = MessageDigest.getInstance("SHA-512");
             *
             *      byte[] az = hash.digest(bytePwd);
             *      az[0] &= 248;
             *      az[31] &= 63;
             *      az[31] |= 64;
             */
            /*** C
             *      unsigned char az[64];
             *
             *      crypto_hash_sha512(az,pw,pwlen);
             *      az[0] &= 248;
             *      az[31] &= 63;
             *      az[31] |= 64;
             */

            using (var sha512 = SHA512.Create())
            {
                byte[] az = sha512.ComputeHash(passwordBytes);
                ScalarOperations.sc_clamp(az, 0);

                /*** Java
                 *      int mlen = seed.length;
                 *      final byte[] sm = new byte[64 + mlen];
                 *
                 *      System.arraycopy(seed, 0, sm, 64, mlen);
                 *      System.arraycopy(az, 32, sm, 32, 32);
                 *
                 *      byte[] buff = Arrays.copyOfRange(sm, 32, 96);
                 *      hash.reset();
                 *      byte[] nonce = hash.digest(buff);
                 */
                /*** C
                 *      unsigned char nonce[64];
                 *      unsigned char hram[64];
                 *
                 *      memmove(sm + 64,m,mlen);
                 *      memmove(sm + 32,az + 32,32);
                 *      crypto_hash_sha512(nonce,sm + 32,mlen + 32);
                 */

                byte[] sm = new byte[64 + authenticationData.Length];
                authenticationData.CopyTo(sm.AsSpan().Slice(64));
                Buffer.BlockCopy(az, 32, sm, 32, 32);
                byte[] nonce = sha512.ComputeHash(sm, 32, authenticationData.Length + 32);

                /*** Java
                 *      ScalarOps scalar = new ScalarOps();
                 *
                 *      EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("Ed25519");
                 *      GroupElement elementAvalue = spec.getB().scalarMultiply(az);
                 *      byte[] elementAarray = elementAvalue.toByteArray();
                 *      System.arraycopy(elementAarray, 0, sm, 32, elementAarray.length);
                 */
                /*** C
                 *      ge_p3 A;
                 *
                 *      ge_scalarmult_base(&A,az);
                 *      ge_p3_tobytes(sm + 32,&A);
                 */

                GroupOperations.ge_scalarmult_base(out var A, az, 0);
                GroupOperations.ge_p3_tobytes(sm, 32, ref A);

                /*** Java
                 *      nonce = scalar.reduce(nonce);
                 *      GroupElement elementRvalue = spec.getB().scalarMultiply(nonce);
                 *      byte[] elementRarray = elementRvalue.toByteArray();
                 *      System.arraycopy(elementRarray, 0, sm, 0, elementRarray.length);
                 */
                /*** C
                 *      ge_p3 R;
                 *
                 *      sc_reduce(nonce);
                 *      ge_scalarmult_base(&R,nonce);
                 *      ge_p3_tobytes(sm,&R);
                 */
                ScalarOperations.sc_reduce(nonce);
                GroupOperations.ge_scalarmult_base(out var R, nonce, 0);
                GroupOperations.ge_p3_tobytes(sm, 0, ref R);

                /*** Java
                 *      hash.reset();
                 *      byte[] hram = hash.digest(sm);
                 *      hram = scalar.reduce(hram);
                 *      byte[] tt = scalar.multiplyAndAdd(hram, az, nonce);
                 *      System.arraycopy(tt, 0, sm, 32, tt.length);
                 *
                 *      return Arrays.copyOfRange(sm, 0, 64);
                 */
                /*** C
                 *      unsigned char hram[64];
                 *
                 *      crypto_hash_sha512(hram,sm,mlen + 64);
                 *      sc_reduce(hram);
                 *      sc_muladd(sm + 32,hram,az,nonce);
                 *
                 *      return 0;
                 */
                var hram = sha512.ComputeHash(sm);
                ScalarOperations.sc_reduce(hram);
                var temp = new byte[32];
                ScalarOperations.sc_muladd(temp, hram, az, nonce);
                Buffer.BlockCopy(temp, 0, sm, 32, temp.Length);

                var result = new byte[64];
                Buffer.BlockCopy(sm, 0, result, 0, result.Length);
                return(result);
            }
        }