Exemplo n.º 1
0
        public static void Execute(BitcoinSecret privateKey)
        {
            // PRNG (Pseudo-Random-Number-Generator)
            // add entropy to the PRNG output that NBitcoin is using
            RandomUtils.AddEntropy("Hello");
            RandomUtils.AddEntropy(new byte[] { 1, 2, 3 });
            Key nsaProofKey = new Key();

            // KDF (Key Derivation Function)
            byte[] derived = SCrypt.BitcoinComputeDerivedKey("Hello", new byte[] { 1, 2, 3 });
            RandomUtils.AddEntropy(derived);

            string password = "******";
            BitcoinEncryptedSecret encryptedBitcoinPrivateKey = privateKey.Encrypt(password);
            BitcoinSecret          decryptedBitcoinPrivateKey = encryptedBitcoinPrivateKey.GetSecret(password);

            Console.WriteLine("-------------------Key Derivation Function------------------");
            Console.WriteLine("BitcoinPrivateKey : " + privateKey);
            Console.WriteLine("BitcoinPrivateKey(encrypted) : " + encryptedBitcoinPrivateKey);
            Console.WriteLine("BitcoinPrivateKey(decrypted) : " + decryptedBitcoinPrivateKey);

            // BIP38 (Part 2)
            string mySecret = "Secret Phrase";
            BitcoinPassphraseCode passphraseCode     = new BitcoinPassphraseCode(mySecret, Network.Main, null);
            EncryptedKeyResult    encryptedKeyResult = passphraseCode.GenerateEncryptedSecret();
            var generatedAddress  = encryptedKeyResult.GeneratedAddress;
            var encryptedKey      = encryptedKeyResult.EncryptedKey;
            var confirmationCode  = encryptedKeyResult.ConfirmationCode;
            var bitcoinPrivateKey = encryptedKey.GetSecret(mySecret);

            Console.WriteLine("---------------------BIP38 (Part 2)---------------------");
            Console.WriteLine("check generatedAddress: " + confirmationCode.Check(mySecret, generatedAddress));   // True
            Console.WriteLine("check generatedAddress: " + (bitcoinPrivateKey.GetAddress() == generatedAddress)); // True
            Console.WriteLine("bitcoinPrivateKey: " + bitcoinPrivateKey);
        }
Exemplo n.º 2
0
 public void CanAddEntropyToRandom()
 {
     RandomUtils.AddEntropy(new byte[] { 1, 2, 3 });
     for (int i = 0; i < 100; i++)
     {
         Assert.Equal(50, RandomUtils.GetBytes(50).Length);
     }
 }
Exemplo n.º 3
0
        public void AddEntropy()
        {
            RandomUtils.AddEntropy("hello");
            RandomUtils.AddEntropy(new byte[] { 1, 2, 3 });
            var nsaProofKey = new Key();

            Console.WriteLine("nsaProofKey: " + nsaProofKey);
        }
Exemplo n.º 4
0
        public void CreatePrivateKey()
        {
            RandomUtils.AddEntropy("key-creation-test");
            RandomUtils.AddEntropy(new byte[] { 99, 98, 97 });

            Key privateKey = new Key();

            Console.WriteLine($"Private key : {privateKey.GetWif(Network.Main)}");
        }
Exemplo n.º 5
0
        public App(Action <ContainerBuilder> configDI)
        {
            _configDI = configDI ?? throw new ArgumentNullException(nameof(configDI));
            InitializeComponent();


            // Adds entropy to the random utility used by NBitcoin
            RandomUtils.AddEntropy($"{DateTime.Now.Ticks}-{Device.RuntimePlatform}");

            Iconize.With(new MaterialModule());

            Init();
        }
Exemplo n.º 6
0
        private void btnNew_Click(object sender, RoutedEventArgs e)
        {
            SecureString secureStr = new SecureString();

            for (int i = 0; i < txtRandom.ToString().Length; i++)
            {
                secureStr.AppendChar(txtRandom.ToString()[i]);
            }
            secureStr.MakeReadOnly();

            var functions = new Functions();

            wordList = functions.SelectedLanguage(cboLanguage.SelectedValue.ToString());
            RandomUtils.AddEntropy(secureStr.ToString());
            Mnemonic mnemo = new Mnemonic(wordList, WordCount.TwentyFour);

            txtMnemonic.Text       = mnemo.ToString();
            btnCalculate.IsEnabled = true;
        }
Exemplo n.º 7
0
        public static void CreatingAMoreRandomKey()
        {
            // Uses the RNGCryptoServiceProvider to generate private keys.
            // Malware can modify your PRNG and predict the numbers generated.
            // Most PRNG uses a Seed then generates a series of random numbers from it.
            // The amount of randomness of the seed is defined by a measure called Entropy.
            // The amount of Entropy also depends on the observer.

            // Say you generate a seed from your clock time.
            // Assuming that your clock has a resolution of 1ms (really more ~15ms).
            // If your attacker knows that you generated the key last week...
            // ... your seed has 1000 * 60 * 60 * 24 * 7 = 604800000 possibilities
            // The Entropy for the attacker is log2(604800000) = 29.17 bits.
            // Enumerating such a number on a home computer takes less than 2 seconds.

            // Let's say you use the clock time + process id for generating the seed.
            // There are 1024 different process ids
            // Now there are 604800000 * 1024 possibilies, which takes around 2000 seconds to enumerate.
            // Now let's add the time when I turned on my computer today, assuming the attack knows I turned it on today.
            // Now it is 604800000 * 1024 * 86400000 = 5.35088E+19 possibilities.
            // Though if the attacker has infiltrated your computer they can get that last piece of info and reduce entropy.
            // Entropy = log2(possibilities) so the Entropy of this new number is 65 bits.

            // The hash of a public key is 20 bytes (160 bits), it is smaller than the total universe of the addresses.
            // You might do better.
            // Note: Adding entropy is linearly harder, cracking entropy is exponentially harder.
            // An interesting way of generating entropy quickly is by incorporating human intervention, such as moving the mouse.
            // If you don't completely trust platform PRNG, you can add entropy to the PRNG output used by NBitcoin.

            RandomUtils.AddEntropy("hello");
            RandomUtils.AddEntropy(new byte[] { 1, 2, 3 });
            var nsaProofKey = new Key();

            // additionalEntropy = SHA(SHA(data) ^ additionalEntropy)
            // result = (SHA(PRNG() ^ additionalEntropy)
        }
Exemplo n.º 8
0
        private static void KeyGenerationAndEncryption()
        {
            // 02.01. Key Generation
            RandomUtils.AddEntropy("hello");
            RandomUtils.AddEntropy(new byte[] { 1, 2, 3 });
            var nsaProofKey = new Key();

            Console.WriteLine("Key with entropy " + nsaProofKey.GetBitcoinSecret(Network.TestNet));

            var derived = SCrypt.BitcoinComputeDerivedKey("hello", new byte[] { 1, 2, 3 });

            RandomUtils.AddEntropy(derived);

            var privateKey = new Key();
            var privateKeyBitcoinSecret = privateKey.GetWif(Network.TestNet);

            Console.WriteLine(privateKeyBitcoinSecret); // L1tZPQt7HHj5V49YtYAMSbAmwN9zRjajgXQt9gGtXhNZbcwbZk2r
            BitcoinEncryptedSecret bitcoinEncryptedSecret = privateKeyBitcoinSecret.Encrypt("password");

            Console.WriteLine(bitcoinEncryptedSecret); // 6PYKYQQgx947Be41aHGypBhK6TA5Xhi9TdPBkatV3fHbbKrdDoBoXFCyLK
            var decryptedBitcoinPrivateKey = bitcoinEncryptedSecret.GetSecret("password");

            Console.WriteLine(decryptedBitcoinPrivateKey); // L1tZPQt7HHj5V49YtYAMSbAmwN9zRjajgXQt9gGtXhNZbcwbZk2r

            //02.02 BIP38 and HD (Hierarchical Deterministic) Wallet
            var passphraseCode = new BitcoinPassphraseCode("my secret", Network.TestNet, null);
            // we give this passphraseCode to 3rd party
            EncryptedKeyResult encryptedKeyResult = passphraseCode.GenerateEncryptedSecret();
            var generatedAddress     = encryptedKeyResult.GeneratedAddress;
            var encryptedKeySecretEc = encryptedKeyResult.EncryptedKey;
            var confirmationCode     = encryptedKeyResult.ConfirmationCode;

            Console.WriteLine(confirmationCode.Check("my secret", generatedAddress)); // True
            var privateKeyBitcoinSecret1 = encryptedKeySecretEc.GetSecret("my secret");

            Console.WriteLine(privateKeyBitcoinSecret1.GetAddress() == generatedAddress); // True
            Console.WriteLine(privateKeyBitcoinSecret1);                                  // KzzHhrkr39a7upeqHzYNNeJuaf1SVDBpxdFDuMvFKbFhcBytDF1R

            ExtKey masterKey = new ExtKey();

            Console.WriteLine("Master key : " + masterKey.ToString(Network.TestNet));
            for (int i = 0; i < 5; i++)
            {
                ExtKey extKey1 = masterKey.Derive((uint)i);
                Console.WriteLine("Key " + i + " : " + extKey1.ToString(Network.TestNet));
            }

            ExtKey extKey = new ExtKey();

            byte[] chainCode = extKey.ChainCode;
            Key    key       = extKey.PrivateKey;

            ExtKey newExtKey = new ExtKey(key, chainCode);

            ExtPubKey masterPubKey = masterKey.Neuter();

            for (int i = 0; i < 5; i++)
            {
                ExtPubKey pubkey = masterPubKey.Derive((uint)i);
                Console.WriteLine("PubKey " + i + " : " + pubkey.ToString(Network.TestNet));
            }

            ExtKey key1 = masterKey.Derive(4);

            //Check it is legit
            var generatedAddr = masterPubKey.Derive(4).PubKey.GetAddress(Network.TestNet);
            var expectedAddr  = key1.PrivateKey.PubKey.GetAddress(Network.TestNet);

            Console.WriteLine("Generated address : " + generatedAddr);
            Console.WriteLine("Expected address : " + expectedAddr);

            ExtKey parent      = new ExtKey();
            ExtKey child11     = parent.Derive(1).Derive(1);
            ExtKey sameChild11 = parent.Derive(new KeyPath("1/1"));

            Console.WriteLine("child11 : " + child11.PrivateKey.PubKey.GetAddress(Network.TestNet));
            Console.WriteLine("child11.PrivateKey == sameChild11.PrivateKey : " +
                              (child11.PrivateKey == sameChild11.PrivateKey));

            ExtKey ceoKey = new ExtKey();

            Console.WriteLine("CEO: " + ceoKey.ToString(Network.TestNet));
            ExtKey notHardenedAccountingKey = ceoKey.Derive(0, hardened: false);

            ExtPubKey ceoPubkey = ceoKey.Neuter();

            //Recover ceo key with accounting private key and ceo public key
            ExtKey ceoKeyRecovered = notHardenedAccountingKey.GetParentExtKey(ceoPubkey);

            Console.WriteLine("CEO recovered: " + ceoKeyRecovered.ToString(Network.TestNet));

            ExtKey hardenedAccountingKey = ceoKey.Derive(0, hardened: true);
            // ExtKey ceoKeyRecovered2 = hardenedAccountingKey.GetParentExtKey(ceoPubkey); => //throws exception

            Mnemonic mnemo  = new Mnemonic(Wordlist.English, WordCount.Twelve); // generate random 12 words list
            ExtKey   hdRoot = mnemo.DeriveExtKey("my password");

            mnemo = new Mnemonic("minute put grant neglect anxiety case globe win famous correct turn link",
                                 Wordlist.English);
            hdRoot = mnemo.DeriveExtKey("my password");
            Console.WriteLine(mnemo);
            Console.WriteLine(hdRoot.ToString(Network.TestNet));

            var scanKey  = new Key();
            var spendKey = new Key();
            BitcoinStealthAddress stealthAddress
                = new BitcoinStealthAddress
                  (
                      scanKey: scanKey.PubKey,
                      pubKeys: new[] { spendKey.PubKey },
                      signatureCount: 1,
                      bitfield: null,
                      network: Network.TestNet);

            Transaction transaction = new Transaction();

            stealthAddress.SendTo(transaction, Money.Coins(1.0m));
            Console.WriteLine(transaction);

            // personal tests Mycelium
            mnemo  = new Mnemonic("artist tiger always access sport major donkey coil scale carry laptop ticket", Wordlist.English);
            hdRoot = mnemo.DeriveExtKey();//leave the password null as sample
            Console.WriteLine(hdRoot.ToString(Network.Main));
            var hardened2 = new KeyPath("44'/0'/0'/0/1");

            ExtKey paymentKey2 = hdRoot.Derive(hardened2);

            Console.WriteLine(hardened2 + ": " + paymentKey2.ScriptPubKey.GetDestinationAddress(Network.Main));
            Console.WriteLine(hardened2 + ": private " + paymentKey2.ToString(Network.Main));

            var    hardened1   = new KeyPath("44'/0'/0'/0/0");
            ExtKey paymentKey1 = hdRoot.Derive(hardened1);

            Console.WriteLine(hardened1 + ": " + paymentKey1.ScriptPubKey.GetDestinationAddress(Network.Main));
            Console.WriteLine(hardened1 + ": private " + paymentKey1.ToString(Network.Main));
        }
        private void buttonGenerateFromFile_Click(object sender, EventArgs e)
        {
            DateTime dtStart;

            if (!StartQuery(out dtStart))
            {
                return;
            }
            try
            {
                if (!File.Exists(filePath.Trim()))
                {
                    ErrorMessage("файл " + filePath.Trim() + " не найден");
                    return;
                }
                StartLoopProcess(true);

                string delem = textBoxDelimiter.Text;

                StringBuilder sb = new StringBuilder();

                int count = 0;

                ProgressBarCounter = 0;
                timerThread.Start();

                cts = new CancellationTokenSource();
                ParallelOptions po = new ParallelOptions()
                {
                    MaxDegreeOfParallelism = 8,
                    CancellationToken      = cts.Token,
                };
                try
                {
                    Parallel.ForEach(File.ReadLines(filePath.Trim()), po, (line, state) =>
                    {
                        if (line.Trim() != String.Empty)
                        {
                            var network = Network.Main;

                            RandomUtils.AddEntropy(line.Trim());
                            var nsaProofKey       = new Key();
                            var privateKey        = new Key();
                            var bitcoinPrivateKey = privateKey.GetWif(network);
                            var address           = bitcoinPrivateKey.GetAddress();

                            lock (sb)
                            {
                                sb.AppendFormat("{0}{2}{1}", bitcoinPrivateKey, address, delem);
                                sb.AppendLine();
                                count++;
                                Interlocked.Increment(ref ProgressBarCounter);
                            }
                        }
                        Application.DoEvents();
                        if (StopProcess)
                        {
                            state.Stop();
                        }
                    });
                }
                catch (OperationCanceledException ex)
                {
                    StartProcess(false, 0);
                    StatusMessage("Запрошена отмена потоков: " + ex.Message);
                    return;
                }


                textBoxText.Text = sb.ToString();
                StatusMessage("Обработано " + count + " линий");
            }
            catch (Exception ex)
            {
                ErrorMessage("buttonGenerateFromFile_Click" + ex.Message);
            }
            finally
            {
                StartLoopProcess(false);
                FinallyQueryWork(dtStart);
                EnableControls(true);
            }
        }
        private void buttonGenerateRandom_Click(object sender, EventArgs e)
        {
            DateTime dtStart;

            if (!StartQuery(out dtStart))
            {
                return;
            }
            try
            {
                int count = (int)numericUpDown1.Value;

                StartLoopProcess(true);

                StringBuilder sb = new StringBuilder();

                string delem = textBoxDelimiter.Text;

                ProgressBarCounter = 0;
                timerThread.Start();

                cts = new CancellationTokenSource();
                ParallelOptions po = new ParallelOptions()
                {
                    MaxDegreeOfParallelism = 8,
                    CancellationToken      = cts.Token,
                };
                try
                {
                    Parallel.For(0, count, po, (i, pls1) =>
                    {
                        var network = Network.Main;

                        RandomUtils.AddEntropy(RandomString(100));
                        var nsaProofKey       = new Key();
                        var privateKey        = new Key();
                        var bitcoinPrivateKey = privateKey.GetWif(network);
                        var address           = bitcoinPrivateKey.GetAddress();
                        var publicKey         = bitcoinPrivateKey.PubKey;
                        lock (sb)
                        {
                            sb.AppendFormat("{0}{3}{1}{3}{2}", bitcoinPrivateKey, address, publicKey, delem);
                            sb.AppendLine();
                            Interlocked.Increment(ref ProgressBarCounter);
                        }

                        Application.DoEvents();
                        if (StopProcess)
                        {
                            pls1.Stop();
                        }
                    });
                }
                catch (OperationCanceledException ex)
                {
                    StartProcess(false, 0);
                    StatusMessage("Запрошена отмена потоков: " + ex.Message);
                    return;
                }

                timerThread.Stop();

                textBoxText.Text = sb.ToString();
                StatusMessage("Обработано " + count + " строк");
            }
            catch (Exception ex)
            {
                ErrorMessage("buttonGenerateRandom_Click" + ex.Message);
            }
            finally
            {
                StartLoopProcess(false);
                FinallyQueryWork(dtStart);
                EnableControls(true);
            }
        }
Exemplo n.º 11
0
        static void Main()
        {
            RandomUtils.Random = new UnsecureRandom();

            //============================================================================================
            //Sectioin. Key generation and encryption



            //============================================================================================
            //Section1. Is it random enough?

            //When you call new Key(), under the hood, you are using a PRNG (Pseudo - Random - Number - Generator) to generate your private key.On windows, it uses the RNGCryptoServiceProvider, a.NET wrapper around the Windows Crypto API.
            //    On Android, I use the SecureRandom class, and in fact, you can use your own implementation with RandomUtils.Random.
            //    On iOS, I have not implemented it and you will need to create your own IRandom implementation.
            //    For a computer, being random is hard.But the biggest issue is that it is impossible to know if a series of numbers is really random.
            //If malware modifies your PRNG so that it can predict the numbers you will generate, you won’t see it until it is too late.
            //It means that a cross platform and naïve implementation of PRNG (like using the computer’s clock combined with CPU speed) is dangerous.But you won’t see it until it is too late.
            //For performance reasons, most PRNG works the same way: a random number which is called a Seed is chosen, then a predictable formula generates the next number each time you ask for it.
            //The amount of randomness of the seed is defined by a measure we call Entropy, but the amount of Entropy also depends on the observer.
            //Let’s say you generate a seed from your clock time.
            //And let’s imagine that your clock has 1ms of resolution. (Reality is more ~15ms.)
            //If your attacker knows that you generated the key last week, then your seed has
            //1000 * 60 * 60 * 24 * 7 = 604800000 possibilities.
            //For such attacker, the entropy is log2(604800000) = 29.17 bits.
            //And enumerating such all possibility with corresponding entropy on my home computer took less than 2 seconds. We call such enumeration “brute forcing”.
            //However, let’s say, you use the clock time + the process ID for generating the seed.
            //Let’s imagine that there are 1024 different process IDs.
            //So now, the attacker needs to enumerate 604800000 * 1024 possibilities, which take around 2000 seconds.
            //Now, let’s add the time on it. When I turned on my computer, assuming the attacker knows I turned it on today, it adds 86400000 possibilities.
            //Now the attacker needs to enumerate 604800000 * 1024 * 86400000 = 5,35088E+19 possibilities.
            //However, keep in mind that if the attacker has infiltrated my computer, he can get this last piece of info, and bring down the number of possibilities, reducing entropy.
            //Entropy is measured by log2(possibilities) and so log2(5,35088E+19) = 65 bits.
            //Is it enough? Probably, only when you assuming your attacker does not know more information about the realm of possibilities used to generate the seed.
            //But since the hash of a public key is 20 bytes(160 bits), it is smaller than the total universe of the addresses.You might do better.
            //Note: Adding entropy is linearly harder. On the other hand, cracking entropy is exponentially harder.
            //An interesting way of generating entropy quickly is by incorporating human intervention, such as moving the mouse.+


            //If you don’t completely trust the platform PRNG (which is not so paranoic), you can add entropy to the PRNG output that NBitcoin is using.

            //What NBitcoin does when you call AddEntropy(data) is
            //additionalEntropy = SHA(SHA(data)^additionalEntropy)
            RandomUtils.AddEntropy("hello");
            RandomUtils.AddEntropy(new byte[] { 1, 2, 3 });
            //When you generate a new number it's like
            //result = SHA(PRNG()^additionalEntropy)
            var nsaProofKey = new Key();

            Console.WriteLine(nsaProofKey.GetWif(Network.Main));



            //=======================================================================================
            //Section2. Key Derivation Function

            //However, what is most important is not the number of possibilities.It is the time that an attacker would need to successfully break your key. That’s where KDF enters the game.
            //KDF, or Key Derivation Function, is a way to have a stronger key, even if your entropy is low.
            //Imagine that you want to generate a seed, and the attacker knows that there are 10,000,000 possibilities.
            //Such a seed would be normally cracked pretty easily.
            //But what if you could make the enumeration slower?
            //A KDF is a hash function that wastes computing resources on purpose.
            //Here is an example:
            var derived = SCrypt.BitcoinComputeDerivedKey("hello", new byte[] { 1, 2, 3 });

            RandomUtils.AddEntropy(derived);



            //Even if your attacker knows that your source of entropy is 5 letters, he will need to run Scrypt to check each possibility, which takes 5 seconds on my computer.
            //The bottom line is: There is nothing paranoid in distrusting a PRNG, and you can mitigate an attack by both adding entropy and also using a KDF.
            //Keep in mind that an attacker can decrease entropy by gathering information about you or your system.
            //If you use the timestamp as entropy source, then an attacker can decrease the entropy by knowing the fact that you generated the key last week, and that you only use your computer between 9am and 6pm.
            //In the previous part I talked briefly about a special KDF called Scrypt. As I said, the goal of a KDF is to make "brute forcing" costly.
            //So it should be no surprise for you that a standard already exists for encrypting your private key with a password using a KDF.This is BIP38.

            var privateKey        = new Key();
            var bitcoinPrivateKey = privateKey.GetWif(Network.Main);

            Console.WriteLine(bitcoinPrivateKey);
            //Output:
            //L1tZPQt7HHj5V49YtYAMSbAmwN9zRjajgXQt9gGtXhNZbcwbZk2r
            BitcoinEncryptedSecret encryptedBitcoinPrivateKey = bitcoinPrivateKey.Encrypt("password");

            Console.WriteLine(encryptedBitcoinPrivateKey);
            //Output:
            //6PYKYQQgx947Be41aHGypBhK6TA5Xhi9TdPBkatV3fHbbKrdDoBoXFCyLK
            var decryptedBitcoinPrivateKey = encryptedBitcoinPrivateKey.GetSecret("password");

            Console.WriteLine(decryptedBitcoinPrivateKey);
            //Output:
            //L1tZPQt7HHj5V49YtYAMSbAmwN9zRjajgXQt9gGtXhNZbcwbZk2r



            //Such encryption is used in two different cases:
            //You do not trust your storage provider(they can get hacked)
            //You are storing the key on the behalf of somebody else (and you do not want to know their key)
            //If you own your storage, then encrypting at the database level might be enough.
            //Be careful if your server takes care of decrypting the key. An attacker might attempt a DDoS attack to your server by forcing it to decrypt lots of keys.
            //Delegate decryption to the ultimate end user when you can.



            //=======================================================================================
            //Section3. Like the good ol’ days

            //First, why generate several keys?
            //The main reason is privacy.Since you can see the balance of all addresses, it is better to use a new address for each transaction.
            //However, in practice, you can also generate keys for each contact which makes this a simple way to identify your payer without leaking too much privacy.
            //You can generate a key, like you did at the beginning:
            var privateKey1 = new Key();


            //However, you have two problems with that:
            //1.All backups of your wallet that you have will become outdated when you generate a new key.
            //2.You cannot delegate the address creation process to an untrusted peer.
            //If you are developing a web wallet and generate keys on behalf of your users, and one user get hacked, they will immediately start suspecting you.
        }
Exemplo n.º 12
0
        private static void Main(string[] args)
        {
            // add entropy before creating key
            RandomUtils.AddEntropy("hello");
            RandomUtils.AddEntropy(new byte[] { 1, 2, 3 });

            // key created with added entropy
            var nsaProofKey = new Key();

            // What NBitcoin does when you call AddEntropy(data) is:
            // additionalEntropy = SHA(SHA(data) ^ additionalEntropy)

            // Then when you generate a new number:
            // result = SHA(PRNG() ^ additionalEntropy)

            // Key Derivation Function is a way to have a stronger key, even if your entropy is low
            // KDF is a hash function that wastes computing resources on purpose.

            var derived = SCrypt.BitcoinComputeDerivedKey("hello", new byte[] { 1, 2, 3 });

            RandomUtils.AddEntropy(derived);

            // even if attacker knows that your source of entropy contains 5 letters,
            // they will need to run Scrypt to check each possibility

            // standard for encrypting private key with a password using kdf is BIP38

            var privateKey        = new Key();
            var bitcoinPrivateKey = privateKey.GetWif(Network.Main);

            Console.WriteLine(bitcoinPrivateKey);

            BitcoinEncryptedSecret encryptedBitcoinPrivateKey = bitcoinPrivateKey.Encrypt("password");

            Console.WriteLine(encryptedBitcoinPrivateKey);

            var decryptedBitcoinPrivateKey = encryptedBitcoinPrivateKey.GetKey("password");

            Console.WriteLine(decryptedBitcoinPrivateKey);

            Key keyFromIncorrectPassword = null;

            Exception error = null;

            try
            {
                keyFromIncorrectPassword = encryptedBitcoinPrivateKey.GetKey("lahsdlahsdlakhslkdash");
            }
            catch (Exception e)
            {
                error = e;
            }

            var result = keyFromIncorrectPassword != null
                ? keyFromIncorrectPassword.ToString()
                : $"{error?.GetType().Name ?? "Error"}: icorrect password";

            Console.WriteLine(result);


            // BIP 38
            // how to delegate Key and Address creation to an untrusted peer

            // create pass phrase code
            BitcoinPassphraseCode passphraseCode = new BitcoinPassphraseCode("my secret", Network.Main, null);

            Console.WriteLine(passphraseCode);

            // then give passPhraseCode to 3rd party key generator
            // third party can generate encrypted keys on your behalf
            // without knowing your password and private key.
            EncryptedKeyResult encryptedKeyResult = passphraseCode.GenerateEncryptedSecret();
            var generatedAddress = encryptedKeyResult.GeneratedAddress;
            var encryptedKey     = encryptedKeyResult.EncryptedKey;

            // used by 3rd party to confirm generated key and address correspond to password
            var confirmationCode = encryptedKeyResult.ConfirmationCode;

            // check
            var isValid = confirmationCode.Check("my secret", generatedAddress);

            Console.WriteLine(isValid);

            var bitcoinPrivateKeyFromPassphraseCode = encryptedKey.GetSecret("my secret");

            Console.WriteLine(bitcoinPrivateKeyFromPassphraseCode.GetAddress() == generatedAddress);

            Console.WriteLine(bitcoinPrivateKey);

            // BIP 32
            // hierarchical deterministic wallets
            // prevent outdated backups

            var masterKey = new ExtKey();


            Console.WriteLine($"Master Key: {masterKey.ToString(Network.Main)}");

            for (uint i = 0; i < 5; i++)
            {
                ExtKey nextKey = masterKey.Derive(i);
                Console.WriteLine($"Key {i} : {nextKey.ToString(Network.Main)}");
            }

            // go back from a Key to ExtKey by supplying the Key and the ChainCode to the ExtKey constructor.

            var extKey = new ExtKey();

            byte[] chainCode = extKey.ChainCode;
            Key    key       = extKey.PrivateKey;

            var newExtKey = new ExtKey(key, chainCode);

            // the base58 type equivalent of ExtKey is BitcoinExtKey


            // "neuter" master key so 3rd party can generate public keys without knowing private key
            ExtPubKey masterPubKey = masterKey.Neuter();

            for (uint i = 0; i < 5; i++)
            {
                ExtPubKey pubKey = masterPubKey.Derive(i);
                Console.WriteLine($"PubKey {i} : {pubKey.ToString(Network.Main)}");
            }

            // get corresponding private key with master key
            masterKey    = new ExtKey();
            masterPubKey = masterKey.Neuter();

            // 3rd party generates pubkey1
            ExtPubKey pubKey1 = masterPubKey.Derive(1);

            // get privatekey of pubKey1
            ExtKey key1 = masterKey.Derive(1);

            Console.WriteLine($"Generated address: {pubKey1.PubKey.GetAddress(Network.Main)}");
            Console.WriteLine($"Expected address: {key1.PrivateKey.PubKey.GetAddress(Network.Main)}");

            // deterministic keys

            // derive the 1st child of the 1st child

            ExtKey parent = new ExtKey();

            // method 1:
            ExtKey child11 = parent.Derive(1).Derive(1);

            // method 2:
            child11 = parent.Derive(new KeyPath("1/1"));

            // why use HD wallets ?
            // easier control, easier to classify keys for multiple accounts

            // but child keys can recover parent key (non-hardened)

            ExtKey ceoExtKey = new ExtKey();

            Console.WriteLine($"CEO: {ceoExtKey.ToString(Network.Main)}");
            ExtKey accountingKey = ceoExtKey.Derive(0, hardened: false);

            ExtPubKey ceoPubKey = ceoExtKey.Neuter();

            // recover ceo key with accounting private key and ceo public key
            ExtKey ceoKeyRecovered = accountingKey.GetParentExtKey(ceoPubKey);

            Console.WriteLine($"CEO recovered: {ceoKeyRecovered.ToString(Network.Main)}");

            // create a hardened key
            var privateCeoExtKey = new ExtKey();

            Console.WriteLine($"Private CEO: {privateCeoExtKey.ToString(Network.Main)}");
            var assitantExtKey = privateCeoExtKey.Derive(1, hardened: true);

            ExtPubKey privateCeoPubKey = privateCeoExtKey.Neuter();

            ExtKey privateCeoKeyRecovered = null;

            try
            {
                privateCeoKeyRecovered = assitantExtKey.GetParentExtKey(privateCeoPubKey);
            }
            catch (Exception e)
            {
                Console.WriteLine($"{e.Message}");
            }

            // creating hardened keys via keypath
            var isNonHardened = new KeyPath("1/2/3").IsHardened;

            Console.WriteLine(isNonHardened);

            var isHardened = new KeyPath("1/2/3'").IsHardened;

            Console.WriteLine(isHardened);

            // imagine that the Accounting Department generates 1 parent key for each customer, and a child for each of the customer’s payments.
            // As the CEO, you want to spend the money on one of these addresses.

            var     accountingCeoKey = new ExtKey();
            string  accounting       = "1'"; // hardened with apostrophe
            int     customerId       = 5;
            int     paymentId        = 50;
            KeyPath path             = new KeyPath($"{accounting}/{customerId}/{paymentId}");

            // path: 1/5/50
            ExtKey paymentKey = accountingCeoKey.Derive(path);

            Console.WriteLine(paymentKey);


            // mnemonic code for HD keys BIP 39
            // used for easy to write keys
            Mnemonic mnemo  = new Mnemonic(Wordlist.English, WordCount.Twelve);
            ExtKey   hdRoot = mnemo.DeriveExtKey("my password");

            Console.WriteLine(mnemo);

            // recover hdRoot with mnemonic and password
            mnemo = new Mnemonic(mnemo.ToString(), Wordlist.English);
            ExtKey recoverdHdRoot = mnemo.DeriveExtKey("my password");

            Console.WriteLine(hdRoot.PrivateKey == recoverdHdRoot.PrivateKey);


            // dark wallet
            // Prevent outdated backups
            // Delegate key / address generation to an untrusted peer

            // bonus feature: only share one address (StealthAddress)

            var scanKey        = new Key();
            var spendKey       = new Key();
            var stealthAddress = new BitcoinStealthAddress(
                scanKey: scanKey.PubKey,
                pubKeys: new[] { spendKey.PubKey },
                signatureCount: 1,
                bitfield: null,
                network: Network.Main
                );

            // payer will take StealthAddress and generate temp key called Ephem Key, and generate a Stealth Pub Key
            // then they package the Ephem PubKey in Stealth Metadata obj embedded in OP_RETURN
            // they will also add the output to the generated bitcoin address (Stealth pub key)

            //The creation of the EphemKey is an implementation detail
            // it can be omitted as NBitcoin will generate one automatically:
            var ephemKey    = new Key();
            var transaction = new Transaction();

            stealthAddress.SendTo(transaction, Money.Coins(1.0m), ephemKey);
            Console.WriteLine(transaction);

            // the Scanner knows the StealthAddress
            // and recovers the Stealth PubKey and Bitcoin Address with the Scan Key

            // scanner then checks if if one of the tx corresponds to the address
            // if true, Scanner notifies the Receiver about tx

            // the receiver can get the private key of the address with their spend key

            // note: a StealthAddress can have mulitple spend pubkeys 9multi sig)

            // limit: use of OP_RETURN makes embedding data in tx difficult
            // OP_RETURN limit is 40 bytes

            Console.ReadLine();
        }