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); }
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 }
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 }
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 }
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); }
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); }
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); }
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"); } }
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); }
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)); }
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 }); }
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 ); }
public static bool TryCreateFromDer(ReadOnlySpan <byte> privkey, [MaybeNullWhen(false)] out ECPrivKey result) { return(TryCreateFromDer(privkey, null, out result)); }
internal static Crypto.ECDSASignature Sign(this Secp256k1.ECPrivKey key, uint256 h, bool enforceLowR) { return(new Crypto.ECDSASignature(key.Sign(h, enforceLowR, out _))); }
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}"); }
public static bool TryCreate(ReadOnlySpan <byte> b32, [MaybeNullWhen(false)] out ECPrivKey key) { return(TryCreate(b32, null, out key)); }