Exemple #1
0
        // TODO: change Sign*() methods accessibility to internal or add additinal checks (eg. input.legnth == 32)

        /// <summary>
        /// Creates a signature using ECSDSA based on BIP-340.
        /// </summary>
        /// <param name="hash">Hash(m) to use for signing</param>
        /// <param name="key">Private key bytes (must be padded to 32 bytes)</param>
        /// <returns>Signature</returns>
        public Signature SignSchnorr(byte[] hash, byte[] key)
        {
            BigInteger         seckey    = key.ToBigInt(true, true);
            EllipticCurvePoint pubkPoint = MultiplyChecked(seckey, curve.G);

            if (!IsSquare(pubkPoint.Y))
            {
                seckey = curve.N - seckey;
                // The internal ComputeTaggedHash_*() methods assume inputs are 32 byte so it needs to be padded here
                // both hash and key are already 32 byte since the caller is PrivateKey.Sign() method
                byte[] temp  = new byte[32];
                byte[] secBa = seckey.ToByteArray(true, true);
                Buffer.BlockCopy(secBa, 0, temp, 32 - secBa.Length, secBa.Length);
                key = temp;
            }

            using Sha256 sha = new Sha256();
            byte[]     kBa = sha.ComputeTaggedHash_BIPSchnorrDerive(key, hash);
            BigInteger k   = kBa.ToBigInt(true, true) % curve.N;

            if (k == 0)
            {
                // This branch will only happen if k was 0 or N (reduced to 0) which is nearly impossible!
                uint   count        = 1;
                byte[] extraEntropy = new byte[32];
                do
                {
                    // TODO: investigate what is the appropriate approach in this special case.

                    // A very similar approach to Sign() method is used here with the extra entropy but
                    // instead of RFC-6979 TaggedHash is used with a different "tag" and 3x 32-byte inputs
                    extraEntropy[0] = (byte)count;
                    extraEntropy[1] = (byte)(count >> 8);
                    extraEntropy[2] = (byte)(count >> 16);
                    extraEntropy[3] = (byte)(count >> 24);
                    count++;
                    kBa = sha.ComputeTaggedHash("BIPSchnorrDeriveExtraEntropy", key, hash, extraEntropy);
                    k   = kBa.ToBigInt(true, true) % curve.N;
                } while (k == 0);
            }

            EllipticCurvePoint R = MultiplyChecked(k, curve.G);

            if (!IsSquare(R.Y))
            {
                k = curve.N - k;
            }

            BigInteger e = ComputeSchnorrE(R.X.ToByteArray(true, true), pubkPoint, hash);
            BigInteger s = (k + (e * seckey)) % curve.N;

            return(new Signature(R.X, s));
        }