Beispiel #1
0
        internal static Secp256k1.SecpECDSASignature Sign(this Secp256k1.ECPrivKey key, uint256 h, bool enforceLowR, out int recid)
        {
            Span <byte> hash = stackalloc byte[32];

            h.ToBytes(hash);
            byte[] extra_entropy = null;
            Secp256k1.RFC6979NonceFunction nonceFunction = null;
            Span <byte> vchSig = stackalloc byte[Secp256k1.SecpECDSASignature.MaxLength];

            Secp256k1.SecpECDSASignature sig;
            uint counter = 0;
            bool ret     = key.TrySignECDSA(hash, null, out recid, out sig);

            // Grind for low R
            while (ret && sig.r.IsHigh && enforceLowR)
            {
                if (extra_entropy == null || nonceFunction == null)
                {
                    extra_entropy = new byte[32];
                    nonceFunction = new Secp256k1.RFC6979NonceFunction(extra_entropy);
                }
                Utils.ToBytes(++counter, true, extra_entropy.AsSpan());
                ret = key.TrySignECDSA(hash, nonceFunction, out recid, out sig);
            }
            return(sig);
        }
Beispiel #2
0
        public void ReadWrite(BitcoinStream stream)
        {
            AssertNotDiposed();
#if HAS_SPAN
            Span <byte> tmp = stackalloc byte[KEY_SIZE];
            if (!stream.Serializing)
            {
                stream.ReadWrite(ref tmp);
                if (NBitcoinContext.Instance.TryCreateECPrivKey(tmp, out var k) && k is Secp256k1.ECPrivKey)
                {
                    _ECKey = k;
                }
                else
                {
                    throw new FormatException("Unvalid private key");
                }
            }
            else
            {
                _ECKey.WriteToSpan(tmp);
                stream.ReadWrite(ref tmp);
            }
#else
            stream.ReadWrite(ref vch);
            if (!stream.Serializing)
            {
                _ECKey = new ECKey(vch, true);
            }
#endif
        }
Beispiel #3
0
        public Key(byte[] data, int count = -1, bool fCompressedIn = true)
        {
            if (count == -1)
            {
                count = data.Length;
            }
            if (count != KEY_SIZE)
            {
                throw new ArgumentException(paramName: "data", message: $"The size of an EC key should be {KEY_SIZE}");
            }
#if HAS_SPAN
            if (NBitcoinContext.Instance.TryCreateECPrivKey(data.AsSpan().Slice(0, KEY_SIZE), out var key) && key is Secp256k1.ECPrivKey)
            {
                IsCompressed = fCompressedIn;
                _ECKey       = key;
            }
            else
            {
                throw new ArgumentException(paramName: "data", message: "Invalid EC key");
            }
#else
            if (Check(data))
            {
                vch          = data.SafeSubarray(0, count);
                IsCompressed = fCompressedIn;
                _ECKey       = new ECKey(vch, true);
            }
            else
            {
                throw new ArgumentException(paramName: "data", message: "Invalid EC key");
            }
#endif
        }
Beispiel #4
0
        public Key(bool fCompressedIn)
        {
            IsCompressed = fCompressedIn;
#if HAS_SPAN
            Span <byte> data = stackalloc byte[KEY_SIZE];
            while (true)
            {
                RandomUtils.GetBytes(data);
                if (NBitcoinContext.Instance.TryCreateECPrivKey(data, out var key) && key is Secp256k1.ECPrivKey)
                {
                    _ECKey = key;
                    return;
                }
            }
#else
            var data = new byte[KEY_SIZE];
            do
            {
                RandomUtils.GetBytes(data);
            } while (!Check(data));

            vch          = data.SafeSubarray(0, data.Length);
            IsCompressed = fCompressedIn;
            _ECKey       = new ECKey(vch, true);
#endif
        }
Beispiel #5
0
        public static bool TryCreateFromDer(ReadOnlySpan <byte> privkey, Context?ctx, [MaybeNullWhen(false)] out ECPrivKey result)
        {
            result = null;
            Span <byte> out32 = stackalloc byte[32];
            int         lenb  = 0;
            int         len   = 0;

            out32.Fill(0);
            /* sequence header */
            if (privkey.Length < 1 || privkey[0] != 0x30)
            {
                return(false);
            }
            privkey = privkey.Slice(1);
            /* sequence length constructor */
            if (privkey.Length < 1 || (privkey[0] & 0x80) == 0)
            {
                return(false);
            }
            lenb    = privkey[0] & ~0x80;
            privkey = privkey.Slice(1);
            if (lenb < 1 || lenb > 2)
            {
                return(false);
            }
            if (privkey.Length < lenb)
            {
                return(false);
            }
            /* sequence length */
            len     = privkey[lenb - 1] | (lenb > 1 ? privkey[lenb - 2] << 8 : 0);
            privkey = privkey.Slice(lenb);
            if (privkey.Length < len)
            {
                return(false);
            }
            /* sequence element 0: version number (=1) */
            if (privkey.Length < 3 || privkey[0] != 0x02 || privkey[1] != 0x01 || privkey[2] != 0x01)
            {
                return(false);
            }
            privkey = privkey.Slice(3);
            /* sequence element 1: octet string, up to 32 bytes */
            if (privkey.Length < 2 || privkey[0] != 0x04 || privkey[1] > 0x20 || privkey.Length < 2 + privkey[1])
            {
                return(false);
            }
            privkey.Slice(2, privkey[1]).CopyTo(out32.Slice(32 - privkey[1]));
            var s = new Scalar(out32, out int overflow);

            if (overflow == 1 || s.IsZero)
            {
                out32.Fill(0);
                result = null;
                return(false);
            }
            result = new ECPrivKey(s, ctx, false);
            return(true);
        }
Beispiel #6
0
 internal Key(Secp256k1.ECPrivKey ecKey, bool compressed)
 {
     if (ecKey == null)
     {
         throw new ArgumentNullException(nameof(ecKey));
     }
     this.IsCompressed = compressed;
     _ECKey            = ecKey;
 }
        public void MultipleSignatureWays()
        {
            var privKeyStr = Encoders.Hex.DecodeData("8e812436a0e3323166e1f0e8ba79e19e217b2c4a53c970d4cca0cfb1078979df");
            Key key        = new Key(privKeyStr);

            Assert.AreEqual("04a5bb3b28466f578e6e93fbfd5f75cee1ae86033aa4bbea690e3312c087181eb366f9a1d1d6a437a9bf9fc65ec853b9fd60fa322be3997c47144eb20da658b3d1", key.PubKey.Decompress().ToHex());

            var messageToSign = "159817a085f113d099d3d93c051410e9bfe043cc5c20e43aa9a083bf73660145";
            var messageBytes  = Encoders.Hex.DecodeData(messageToSign);

            ECDSASignature signature = key.Sign(new uint256(messageBytes), true);

            SecpECDSASignature.TryCreateFromDer(signature.ToDER(), out SecpECDSASignature sig);
            var(r, s) = sig;

            var R = r.ToBytes();
            var S = s.ToBytes();

            Assert.AreEqual("38b7dac5ee932ac1bf2bc62c05b792cd93c3b4af61dc02dbb4b93dacb758123f", R.ToHexString());
            Assert.AreEqual("08bf123eabe77480787d664ca280dc1f20d9205725320658c39c6c143fd5642d", S.ToHexString());

            // Compact signature
            byte[] signatureCompact = key.SignCompact(new uint256(messageBytes), true);
            if (signatureCompact.Length != 65)
            {
                throw new ArgumentException(paramName: nameof(signatureCompact), message: "Signature truncated, expected 65");
            }

            var ss = signatureCompact.AsSpan();

            int recid = (ss[0] - 27) & 3;

            if (!(
                    SecpRecoverableECDSASignature.TryCreateFromCompact(ss.Slice(1), recid, out SecpRecoverableECDSASignature sigR) && sigR is SecpRecoverableECDSASignature
                    )
                )
            {
                throw new InvalidOperationException("Impossible to recover the public key");
            }

            // V from comapct signature
            var(r1, s1, v1) = sigR;

            Assert.AreEqual(v1, 0);

            // Recoverable signature with Secp256k1 lib
            NBitcoin.Secp256k1.ECPrivKey privKey = Context.Instance.CreateECPrivKey(new Scalar(key.ToBytes()));
            Assert.AreEqual(key.PubKey.ToBytes(), privKey.CreatePubKey().ToBytes());

            privKey.TrySignRecoverable(messageBytes, out SecpRecoverableECDSASignature sigRec);

            var(r2, s2, v2) = sigRec;

            Assert.AreEqual(r2, r);
            Assert.AreEqual(s2, s);
            Assert.AreEqual(v2, v1);
        }
Beispiel #8
0
        public static bool TryCreate(ReadOnlySpan <byte> b32, Context?context, [MaybeNullWhen(false)] out ECPrivKey key)
        {
            var s = new Scalar(b32, out var overflow);

            if (overflow != 0 || s.IsZero)
            {
                key = null;
                return(false);
            }
            key = new ECPrivKey(s, context, false);
            return(true);
        }
Beispiel #9
0
 internal Key(ReadOnlySpan <byte> bytes, bool compressed = true)
 {
     this.IsCompressed = compressed;
     if (bytes.Length != KEY_SIZE)
     {
         throw new ArgumentException(paramName: "data", message: $"The size of an EC key should be {KEY_SIZE}");
     }
     if (NBitcoinContext.Instance.TryCreateECPrivKey(bytes, out var key) && key is Secp256k1.ECPrivKey)
     {
         _ECKey = key;
     }
     else
     {
         throw new ArgumentException(paramName: "data", message: "Invalid EC key");
     }
 }
Beispiel #10
0
        public void CreatePubKey()
        {
            Console.WriteLine();

            uint256 N    = uint256.Parse("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
            Random  rand = new Random();

            byte[]  privateKey = new byte[32];
            uint256 candidateKey;

            do
            {
                rand.NextBytes(privateKey);
                candidateKey = new uint256(privateKey, false);
            } while (!(candidateKey > 0 && candidateKey < N));

            // Public key
            privateKey = Encoders.Hex.DecodeData("da7639a9e2ed4e918b57151509ee34b3f80ad4ab60fb52de59cc3a7386b19007");

            NBitcoin.Secp256k1.ECPrivKey privKey = Context.Instance.CreateECPrivKey(new Scalar(privateKey));
            ECPubKey pubKey = privKey.CreatePubKey();

            byte[] pubKeyBytes = pubKey.ToBytes();
            // Console.WriteLine($"Pub key             : {Encoders.Hex.EncodeData(pubKeyBytes)}");

            var x = pubKey.Q.x.ToBytes();
            var y = pubKey.Q.y.ToBytes();

            Console.WriteLine($"Pub key x           :   {Encoders.Hex.EncodeData(x)}");
            Console.WriteLine($"Pub key y           :   {string.Empty.PadLeft(16, '\t')}{Encoders.Hex.EncodeData(y)}");

            var pubKeyUncomp = Helper.Concat(new byte[] { (04) }, x, y);

            Console.WriteLine($"Pub key (uncomp.)   : {Encoders.Hex.EncodeData(pubKeyUncomp)}");

            BigInteger yBig = new BigInteger(y, isUnsigned: true, isBigEndian: true);

            byte pubKeyPrefix = (byte)(yBig % 2 == 0 ? 02 : 03);
            var  pubKeyComp   = Helper.Concat(new byte[] { pubKeyPrefix }, x);

            Console.WriteLine($"Pub key (comp.)     : {Encoders.Hex.EncodeData(pubKeyComp)}");
            Assert.AreEqual(pubKeyBytes, pubKeyComp);
        }
        public void SignWithECPrivKey()
        {
            var privKeyStr = Encoders.Hex.DecodeData("cdce32b32436ff20c2c32ee55cd245a82fff4c2dc944da855a9e0f00c5d889e4");
            Key key        = new Key(privKeyStr);

            NBitcoin.Secp256k1.ECPrivKey privKey = Context.Instance.CreateECPrivKey(new Scalar(key.ToBytes()));

            var messageToSign = "159817a085f113d099d3d93c051410e9bfe043cc5c20e43aa9a083bf73660145";
            var messageBytes  = Encoders.Hex.DecodeData(messageToSign);

            privKey.TrySignRecoverable(messageBytes, out SecpRecoverableECDSASignature sigRec);
            var(r, s, v) = sigRec;

            var R = r.ToBytes().ToHexString();
            var S = s.ToBytes().ToHexString();

            Assert.AreEqual("84de8230e66c6507dea6de6d925c76ac0db85d99ddd3c069659d0970ade8876a", R);
            Assert.AreEqual("0dcd4adb2e40fcf257da419b88c1e7dd4d92750b63381d8379b96f3b7b8a4498", S);
            Assert.AreEqual(1, v);
        }
Beispiel #12
0
        public TaprootSignature SignTaprootKeySpend(uint256 hash, uint256?merkleRoot, uint256?aux, TaprootSigHash sigHash)
        {
            if (hash == null)
            {
                throw new ArgumentNullException(nameof(hash));
            }
            AssertNotDisposed();
            var eckey = _ECKey;

            if (PubKey.Parity)
            {
                eckey = new Secp256k1.ECPrivKey(_ECKey.sec.Negate(), _ECKey.ctx, true);
            }
            Span <byte> buf = stackalloc byte[32];

            TaprootFullPubKey.ComputeTapTweak(PubKey.TaprootInternalKey, merkleRoot, buf);
            eckey = eckey.TweakAdd(buf);
            hash.ToBytes(buf);
            var sig = aux?.ToBytes() is byte[] auxbytes?eckey.SignBIP340(buf, auxbytes) : eckey.SignBIP340(buf);

            return(new TaprootSignature(new SchnorrSignature(sig), sigHash));
        }
Beispiel #13
0
        public Signature Sign(byte[] message)
        {
            if (message.Length != 32)
            {
                throw new ArgumentException(paramName: nameof(message), message: "Message should be 32 bytes");
            }

            NBitcoin.Secp256k1.ECPrivKey privKey = Context.Instance.CreateECPrivKey(new Scalar(PrivateKey.ToBytes()));
            if (!privKey.TrySignRecoverable(message, out SecpRecoverableECDSASignature sigRec))
            {
                throw new InvalidOperationException();
            }

            var(r, s, recId) = sigRec;

            return(new Signature()
            {
                R = r.ToBytes(),
                S = s.ToBytes(),
                RecId = recId
            });
        }
Beispiel #14
0
        public void ShouldEncodeAddress()
        {
            var privateKey = Encoders.Hex.DecodeData("08089C24EC3BAEB34254DDF5297CF8FBB8E031496FF67B4EFACA738FF9EBD455");

            NBitcoin.Secp256k1.ECPrivKey privKey = Context.Instance.CreateECPrivKey(new Scalar(privateKey));
            ECPubKey pubKey = privKey.CreatePubKey();

            var x = pubKey.Q.x.ToBytes();
            var y = pubKey.Q.y.ToBytes();

            var pubKeyUncomp = Helper.Concat(x, y);

            Assert.AreEqual(
                "ee63599802b5d31a29c95cc7df04f427e8f0a124bed9333f3a80404acfc3127659c540d0162dedb81ac5f74b2deb4962656efe112b252e54ac3ba1207cd1fb10",
                Encoders.Hex.EncodeData(pubKeyUncomp)
                );

            var pubKeyHash = new Sha3Keccack().CalculateHash(pubKeyUncomp);

            Assert.AreEqual("0837725ba59e30e8e52ba5ab95679f3aaf5211991781d49b30525dddfe9a18de", pubKeyHash.ToHexString());

            var sha3HashBytes = new byte[20];

            Array.Copy(pubKeyHash, pubKeyHash.Length - 20, sha3HashBytes, 0, 20);

            byte[] PKHWithVersionBytes = Helper.Concat(new byte[] { 65 }, sha3HashBytes);
            var    hexAddress          = PKHWithVersionBytes.ToHexString();

            Assert.AreEqual("4195679F3AAF5211991781D49B30525DDDFE9A18DE".ToLower(), hexAddress);

            var result = Encoders.Base58Check.EncodeData(PKHWithVersionBytes);

            Assert.AreEqual(
                "TPbBpRXnt6ztse8XkCLiJstZyqQZvxW2sx",
                result
                );
        }
Beispiel #15
0
 public static bool TryCreateFromDer(ReadOnlySpan <byte> privkey, [MaybeNullWhen(false)] out ECPrivKey result)
 {
     return(TryCreateFromDer(privkey, null, out result));
 }
Beispiel #16
0
 internal static Crypto.ECDSASignature Sign(this Secp256k1.ECPrivKey key, uint256 h, bool enforceLowR)
 {
     return(new Crypto.ECDSASignature(key.Sign(h, enforceLowR, out _)));
 }
Beispiel #17
0
        public Key Derivate(byte[] cc, uint nChild, out byte[] ccChild)
        {
            AssertNotDisposed();
#if HAS_SPAN
            if (!IsCompressed)
            {
                throw new InvalidOperationException("The key must be compressed");
            }
            Span <byte> vout = stackalloc byte[64];
            vout.Clear();
            if ((nChild >> 31) == 0)
            {
                Span <byte> pubkey = stackalloc byte[33];
                this.PubKey.ToBytes(pubkey, out _);
                Hashes.BIP32Hash(cc, nChild, pubkey[0], pubkey.Slice(1), vout);
            }
            else
            {
                Span <byte> privkey = stackalloc byte[32];
                this._ECKey.WriteToSpan(privkey);
                Hashes.BIP32Hash(cc, nChild, 0, privkey, vout);
                privkey.Fill(0);
            }
            ccChild = new byte[32];
            vout.Slice(32, 32).CopyTo(ccChild);
            Secp256k1.ECPrivKey keyChild = _ECKey.TweakAdd(vout.Slice(0, 32));
            vout.Clear();
            return(new Key(keyChild, true));
#else
            byte[]? l = null;
            if ((nChild >> 31) == 0)
            {
                var pubKey = PubKey.ToBytes();
                l = Hashes.BIP32Hash(cc, nChild, pubKey[0], pubKey.SafeSubarray(1));
            }
            else
            {
                l = Hashes.BIP32Hash(cc, nChild, 0, this.ToBytes());
            }
            var ll = l.SafeSubarray(0, 32);
            var lr = l.SafeSubarray(32, 32);

            ccChild = lr;

            var parse256LL = new BigInteger(1, ll);
            var kPar       = new BigInteger(1, vch);
            var N          = ECKey.CURVE.N;

            if (parse256LL.CompareTo(N) >= 0)
            {
                throw new InvalidOperationException("You won a prize ! this should happen very rarely. Take a screenshot, and roll the dice again.");
            }
            var key = parse256LL.Add(kPar).Mod(N);
            if (key == BigInteger.Zero)
            {
                throw new InvalidOperationException("You won the big prize ! this has probability lower than 1 in 2^127. Take a screenshot, and roll the dice again.");
            }

            var keyBytes = key.ToByteArrayUnsigned();
            if (keyBytes.Length < 32)
            {
                keyBytes = new byte[32 - keyBytes.Length].Concat(keyBytes).ToArray();
            }
            return(new Key(keyBytes));
#endif
        }
        public void CreateAddress()
        {
            Console.WriteLine();

            // Priv key length
            int KEY_SIZE = 32;

            // Max priv key value
            // 115792089237316195423570985008687907852837564279074904382605163141518161494337
            uint256 N = uint256.Parse("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");

            // Randomizer
            Random rand = new Random();

            byte[] privateKey = new byte[KEY_SIZE];

            // Generate a valid random value
            uint256 candidateKey;

            do
            {
                rand.NextBytes(privateKey);
                candidateKey = new uint256(privateKey, false);
            } while (!(candidateKey > 0 && candidateKey < N));
            Console.WriteLine($"Private key (hex)   : { Encoders.Hex.EncodeData(privateKey) }");

            // base58 encoded private key
            byte[] privKeyWithVersionBytes = Helper.Concat(new byte[] { 128 }, privateKey);
            string privKeyBase58           = Encoders.Base58Check.EncodeData(privKeyWithVersionBytes);

            Console.WriteLine($"Priv. key (Base58)  : {privKeyBase58}");

            // base58 encoded compressed private key
            byte[] compPrivKeyWithVersionBytes = Helper.Concat(privKeyWithVersionBytes, new byte[] { 01 });
            var    compPrivKeyBase58           = Encoders.Base58Check.EncodeData(compPrivKeyWithVersionBytes);

            Console.WriteLine($"Priv. key (Base58)  : {compPrivKeyBase58} (Compressed)");

            // Elliptic curve multiplication (with help of NBitcoin)
            // privateKey = Encoders.Hex.DecodeData("da7639a9e2ed4e918b57151509ee34b3f80ad4ab60fb52de59cc3a7386b19007"); // for testing
            NBitcoin.Secp256k1.ECPrivKey privKey = Context.Instance.CreateECPrivKey(new Scalar(privateKey));
            ECPubKey pubKey = privKey.CreatePubKey();

            // X, Y
            var x = pubKey.Q.x.ToBytes();
            var y = pubKey.Q.y.ToBytes();

            // Uncompressed public key
            var pubKeyUncomp = Helper.Concat(new byte[] { (04) }, x, y);

            Console.WriteLine($"Pub key (uncomp.)   : {Encoders.Hex.EncodeData(pubKeyUncomp)}");

            // Compressed public key
            BigInteger yBig       = new BigInteger(y, isUnsigned: true, isBigEndian: true);
            var        pubKeyComp = Helper.Concat(new byte[] { (byte)(yBig % 2 == 0 ? 02 : 03) }, x);

            Console.WriteLine($"Pub key (comp.)     : {Encoders.Hex.EncodeData(pubKeyComp)}");

            //// Uncompressed Public Key
            // Public key hash (pkh)

            var pubKeyHash    = NBitcoin.Crypto.Hashes.SHA256(pubKeyUncomp);
            var pubKeyHash160 = NBitcoin.Crypto.Hashes.RIPEMD160(pubKeyHash, pubKeyHash.Length);

            Console.WriteLine($"Public key hash     : {Encoders.Hex.EncodeData(pubKeyHash160)}");

            // base58 encoded pkh : address
            byte[] PKHWithVersionBytes = Helper.Concat(new byte[] { 00 }, pubKeyHash160);
            var    address             = Encoders.Base58Check.EncodeData(PKHWithVersionBytes);

            Assert.DoesNotThrow(() =>   {
                BitcoinAddress.Create(str: address, Network.Main);
            });
            Console.WriteLine($"Address             : {address}");

            //// Uncompressed Public Key (w/ checksum calculation)
            var hash1           = NBitcoin.Crypto.Hashes.SHA256(PKHWithVersionBytes);
            var hash2           = NBitcoin.Crypto.Hashes.SHA256(hash1);
            var checksum        = hash2.Take(4).ToArray();
            var pkhWithChecksum = Helper.Concat(PKHWithVersionBytes, checksum);

            var address1 = Encoders.Base58.EncodeData(pkhWithChecksum);

            Assert.AreEqual(address, address1);

            //// Compressed Public Key
            // Public key hash (Compressed)
            var pubKeyCompHash    = NBitcoin.Crypto.Hashes.SHA256(pubKeyComp);
            var pubKeyCompHash160 = NBitcoin.Crypto.Hashes.RIPEMD160(pubKeyCompHash, pubKeyCompHash.Length);

            // base58 encoded compressed pkh : address
            byte[] compPKHWithVersionBytes = Helper.Concat(new byte[] { 00 }, pubKeyCompHash160);
            var    addressComp             = Encoders.Base58Check.EncodeData(compPKHWithVersionBytes);

            Assert.DoesNotThrow(() =>   {
                BitcoinAddress.Create(str: addressComp, Network.Main);
            });
            Console.WriteLine($"Address (Comp.)     : {addressComp}");
        }
Beispiel #19
0
 public static bool TryCreate(ReadOnlySpan <byte> b32, [MaybeNullWhen(false)] out ECPrivKey key)
 {
     return(TryCreate(b32, null, out key));
 }