public PublicKey DerivePublickey(PublicKey basepoint, PublicKey perCommitmentPoint)
        {
            // TODO: pubkey = basepoint + SHA256(per_commitment_point || basepoint) * G

            Span <byte> toHash = stackalloc byte[PublicKey.LENGTH * 2];

            perCommitmentPoint.GetSpan().CopyTo(toHash);
            basepoint.GetSpan().CopyTo(toHash.Slice(PublicKey.LENGTH));
            byte[] hashed = NBitcoin.Crypto.Hashes.SHA256(toHash);

            if (ECPubKey.TryCreate(basepoint, Context.Instance, out _, out ECPubKey? ecpubkey))
            {
                if (ecpubkey.TryAddTweak(hashed.AsSpan(), out ECPubKey? ecpubkeytweaked))
                {
                    if (ecpubkeytweaked != null)
                    {
                        Span <byte> pub = stackalloc byte[33];
                        ecpubkeytweaked.WriteToSpan(true, pub, out _);
                        return(new PublicKey(pub.ToArray()));
                    }
                }
            }

            return(null);
        }
        public PublicKey DeriveRevocationPublicKey(PublicKey basepoint, PublicKey perCommitmentPoint)
        {
            // TODO: revocationpubkey = revocation_basepoint * SHA256(revocation_basepoint || per_commitment_point) + per_commitment_point * SHA256(per_commitment_point || revocation_basepoint)

            Span <byte> toHash1 = stackalloc byte[PublicKey.LENGTH * 2];

            basepoint.GetSpan().CopyTo(toHash1);
            perCommitmentPoint.GetSpan().CopyTo(toHash1.Slice(PublicKey.LENGTH));
            byte[] hashed1 = NBitcoin.Crypto.Hashes.SHA256(toHash1);

            ECPubKey?revocationBasepointTweaked = null;

            if (ECPubKey.TryCreate(basepoint, Context.Instance, out _, out ECPubKey? ecbasepoint))
            {
                if (ecbasepoint.TryMultTweak(hashed1.AsSpan(), out ECPubKey? ecpubkeytweaked))
                {
                    if (ecpubkeytweaked != null)
                    {
                        revocationBasepointTweaked = ecpubkeytweaked;
                    }
                }
            }

            Span <byte> toHash2 = stackalloc byte[PublicKey.LENGTH * 2];

            perCommitmentPoint.GetSpan().CopyTo(toHash2);
            basepoint.GetSpan().CopyTo(toHash2.Slice(PublicKey.LENGTH));
            byte[] hashed2 = NBitcoin.Crypto.Hashes.SHA256(toHash2);

            ECPubKey?perCommitmentPointTweaked = null;

            if (ECPubKey.TryCreate(perCommitmentPoint, Context.Instance, out _, out ECPubKey? ecperCommitmentPoint))
            {
                if (ecperCommitmentPoint.TryMultTweak(hashed2.AsSpan(), out ECPubKey? ecperCommitmentPointtweaked))
                {
                    if (ecperCommitmentPointtweaked != null)
                    {
                        perCommitmentPointTweaked = ecperCommitmentPointtweaked;
                    }
                }
            }

            if (revocationBasepointTweaked != null && perCommitmentPointTweaked != null)
            {
                var keys = new ECPubKey[] { revocationBasepointTweaked, perCommitmentPointTweaked };

                if (ECPubKey.TryCombine(Context.Instance, keys, out ECPubKey? revocationpubkey))
                {
                    if (revocationpubkey != null)
                    {
                        Span <byte> pub = stackalloc byte[33];
                        revocationpubkey.WriteToSpan(true, pub, out _);
                        return(new PublicKey(pub.ToArray()));
                    }
                }
            }

            return(null);
        }
 public CompressedPubKey(ECPubKey ecPubKey)
 {
     if (ecPubKey == null)
     {
         throw new ArgumentNullException(nameof(ecPubKey));
     }
     this.EcPubKey = ecPubKey;
 }
 public void UseAdaptor(ECPubKey adaptor)
 {
     if (processed_nonce)
     {
         throw new InvalidOperationException("This function can only be called before MusigContext.Process");
     }
     this.adaptor = adaptor;
 }
Example #5
0
        public static bool TryExtractPrivateKey(this ECPubKey pubKey, ReadOnlySpan <byte> msg1, SecpSchnorrSignature sig1, ReadOnlySpan <byte> msg2, SecpSchnorrSignature sig2, out ECPrivKey?key)
        {
            key = null;
            if (msg1.Length != 32)
            {
                return(false);
            }
            if (msg2.Length != 32)
            {
                return(false);
            }
            if (msg1.SequenceCompareTo(msg2) == 0)
            {
                return(false);
            }

            Span <byte> sig64 = stackalloc byte[64];

            sig1.WriteToSpan(sig64);
            Span <byte> pk_buf = stackalloc byte[32];
            Span <byte> buf    = stackalloc byte[32];

            pubKey.Q.x.WriteToSpan(pk_buf);
            using var sha = new SHA256();
            sha.InitializeTagged(TAG_BIP0340Challenge);
            sha.Write(sig64.Slice(0, 32));
            sha.Write(pk_buf);
            sha.Write(msg1);
            sha.GetHash(buf);

            var n1 = new Scalar(buf, out _);

            sig2.WriteToSpan(sig64);
            sha.InitializeTagged(TAG_BIP0340Challenge);
            sha.Write(sig64.Slice(0, 32));
            sha.Write(pk_buf);
            sha.Write(msg2);
            sha.GetHash(buf);

            var n2 = new Scalar(buf, out _);

            var s  = sig2.s + sig1.s.Negate();
            var n  = (n2 + n1.Negate());
            var sk = s * n.Inverse();

            if (pubKey.Q.y.IsOdd)
            {
                sk = sk.Negate();
            }
            if (!pubKey.ctx.TryCreateECPrivKey(sk, out key))
            {
                return(false);
            }
            return(true);
        }
 public MusigPubNonce(Context?context, ReadOnlySpan <byte> in66)
 {
     if (!ECPubKey.TryCreate(in66.Slice(0, 33), context, out _, out var k1) ||
         !ECPubKey.TryCreate(in66.Slice(33, 33), context, out _, out var k2))
     {
         throw new ArgumentException("Invalid musig pubnonce");
     }
     this.context = context ?? Context.Instance;
     this.k1      = k1;
     this.k2      = k2;
 }
        public PublicKey PublicKeyFromPrivateKey(PrivateKey privateKey)
        {
            if (ECPrivKey.TryCreate(privateKey, Context.Instance, out ECPrivKey? ecprvkey))
            {
                if (ecprvkey != null)
                {
                    ECPubKey    ecpubkey = ecprvkey.CreatePubKey();
                    Span <byte> pub      = stackalloc byte[33];
                    ecpubkey.WriteToSpan(true, pub, out _);
                    return(new PublicKey(pub.ToArray()));
                }
            }

            return(null);
        }
        internal static void secp256k1_musig_compute_noncehash(Span <byte> noncehash, Span <GE> aggnonce, ReadOnlySpan <byte> agg_pk32, ReadOnlySpan <byte> msg)
        {
            Span <byte> buf = stackalloc byte[33];

            using SHA256 sha = new SHA256();
            sha.InitializeTagged("MuSig/noncecoef");
            int i;

            for (i = 0; i < 2; i++)
            {
                ECPubKey.secp256k1_eckey_pubkey_serialize(buf, ref aggnonce[i], out _, true);
                sha.Write(buf);
            }
            sha.Write(agg_pk32.Slice(0, 32));
            sha.Write(msg.Slice(0, 32));
            sha.GetHash(noncehash);
        }
Example #9
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);
        }
Example #10
0
        public static bool SigVerify(this ECPubKey pubKey, SecpECDSAAdaptorSignature sig, SecpECDSAAdaptorProof proof, ReadOnlySpan <byte> msg32, ECPubKey adaptor)
        {
            if (pubKey == null)
            {
                throw new ArgumentNullException(nameof(pubKey));
            }
            if (adaptor == null)
            {
                throw new ArgumentNullException(nameof(adaptor));
            }
            if (msg32.Length < 32)
            {
                throw new ArgumentException(paramName: nameof(msg32), message: "msg32 should be at least 32 bytes");
            }
            if (sig == null)
            {
                throw new ArgumentNullException(nameof(sig));
            }
            var adaptor_ge = adaptor.Q;

            if (!secp256k1_dleq_verify(pubKey.ctx.EcMultContext, "ECDSAAdaptorSig", proof.s, proof.e, proof.rp, adaptor_ge, sig.r))
            {
                return(false);
            }

            /* 2. return x_coord(R') == x_coord(s'⁻¹(H(m) * G + x_coord(R) * X)) */
            var q   = pubKey.Q;
            var msg = new Scalar(msg32);

            if (!pubKey.ctx.secp256k1_ecdsa_adaptor_sig_verify_helper(sig.r.x.ToScalar(), sig.sp, q, msg, out var rhs))
            {
                return(false);
            }
            var lhs = proof.rp.ToGroupElementJacobian();

            rhs = rhs.Negate();
            lhs = lhs.AddVariable(rhs, out _);
            return(lhs.IsInfinity);
        }
Example #11
0
        public static bool TryComputeSigPoint(this ECXOnlyPubKey pubkey, ReadOnlySpan <byte> msg32, SchnorrNonce rx, out ECPubKey?sigpoint)
        {
            if (rx == null)
            {
                throw new ArgumentNullException(nameof(rx));
            }
            if (msg32.Length != 32)
            {
                throw new ArgumentException("Msg should be 32 bytes", nameof(msg32));
            }
            sigpoint = null;
            Span <byte> buf    = stackalloc byte[32];
            Span <byte> pk_buf = stackalloc byte[32];

            pubkey.WriteXToSpan(pk_buf);
            /* tagged hash(r.x, pk.x, msg32) */
            using var sha = new SHA256();
            sha.InitializeTagged(TAG_BIP0340Challenge);
            rx.fe.WriteToSpan(buf);
            sha.Write(buf);
            sha.Write(pk_buf);
            sha.Write(msg32);
            sha.GetHash(buf);
            if (!pubkey.TryMultTweak(buf, out var pubkey_ge) || pubkey_ge is null)
            {
                return(false);
            }
            if (!GE.TryCreateXQuad(rx.fe, out var rx_ge))
            {
                return(false);
            }
            var pubkey_gej   = pubkey_ge.Q.ToGroupElementJacobian();
            var sigpoint_gej = pubkey_gej + rx_ge;
            var sigpoint_ge  = sigpoint_gej.ToGroupElement();

            sigpoint = new ECPubKey(sigpoint_ge, pubkey.ctx);
            return(true);
        }
Example #12
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
                );
        }
        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}");
        }
Example #14
0
        public static bool TrySignAdaptor(this ECPrivKey key, ReadOnlySpan <byte> msg32, ECPubKey adaptor, out SecpECDSAAdaptorSignature?adaptorSignature, out SecpECDSAAdaptorProof?proof)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (adaptor == null)
            {
                throw new ArgumentNullException(nameof(adaptor));
            }
            if (msg32.Length < 32)
            {
                throw new ArgumentException(paramName: nameof(msg32), message: "msg32 should be at least 32 bytes");
            }
            var    adaptor_ge = adaptor.Q;
            var    seckey32   = key.sec;
            SHA256 sha        = new SHA256();

            sha.Write(msg32.Slice(0, 32));
            Span <byte> buf33 = stackalloc byte[33];

            Internals.secp256k1_dleq_serialize_point(buf33, adaptor_ge);
            sha.Write(buf33);
            sha.GetHash(buf33);
            Span <byte> nonce32 = stackalloc byte[32];

            nonce_function_dleq(buf33, key.sec, "ECDSAAdaptorNon", nonce32);
            var k = new Scalar(nonce32);

            if (k.IsZero)
            {
                adaptorSignature = default;
                proof            = default;
                return(false);
            }
            var rpj = key.ctx.EcMultGenContext.MultGen(k);
            /* 2. R = k*Y; */
            var rj = adaptor_ge.MultConst(k, 256);

            /* 4. [sic] proof = DLEQ_prove((G,R'),(Y, R)) */
            if (!key.ctx.EcMultGenContext.secp256k1_dleq_proof("ECDSAAdaptorSig", k, adaptor_ge, out var dleq_proof_s, out var dleq_proof_e))
            {
                adaptorSignature = default;
                proof            = default;
                return(false);
            }

            /* 5. s' = k⁻¹(H(m) + x_coord(R)x) */
            var r   = rj.ToGroupElement();
            var msg = new Scalar(msg32);

            if (!secp256k1_ecdsa_adaptor_sign_helper(msg, k, r, key.sec, out var sp))
            {
                k = default;
                adaptorSignature = default;
                proof            = default;
                return(false);
            }

            /* 6. return (R, R', s', proof) */
            var rp = rpj.ToGroupElement();

            proof            = new SecpECDSAAdaptorProof(rp, dleq_proof_s, dleq_proof_e);
            adaptorSignature = new SecpECDSAAdaptorSignature(r, sp);
            k = default;
            return(true);
        }
 internal MusigPubNonce(ECPubKey k1, ECPubKey k2)
 {
     this.k1      = k1;
     this.k2      = k2;
     this.context = k1.ctx;
 }