Exemple #1
0
        public static void TryUpdateLookAhead(string passphrase, X1WalletFile x1WalletFile)
        {
            if (string.IsNullOrWhiteSpace(passphrase))
            {
                return;
            }

            x1WalletFile.LookAhead.Clear();

            var decryptedSeed     = VCL.DecryptWithPassphrase(passphrase, x1WalletFile.HdSeed);
            var changeAddressGaps = GetIndexGaps(C.Change, x1WalletFile);

            foreach (var index in changeAddressGaps)
            {
                var generated = GeneratePubKeyHashAddress(decryptedSeed, passphrase, x1WalletFile.CoinType, C.Change, index);
                x1WalletFile.LookAhead[generated.Address] = generated;
            }

            var receiveAddressGaps = GetIndexGaps(C.External, x1WalletFile);

            foreach (var index in receiveAddressGaps)
            {
                var generated = GeneratePubKeyHashAddress(decryptedSeed, passphrase, x1WalletFile.CoinType, C.External, index);
                x1WalletFile.LookAhead[generated.Address] = generated;
            }

            x1WalletFile.SaveX1WalletFile(x1WalletFile.CurrentPath);
        }
        internal static ExportKeysResponse ExportKeys(ExportKeysRequest exportKeysRequest, IReadOnlyCollection <ISegWitAddress> addresses)
        {
            var header = new StringBuilder();

            header.AppendLine($"Starting export from wallet {exportKeysRequest.WalletName}, network {C.Network.Name} on {DateTime.UtcNow} UTC.");
            var errors = new StringBuilder();

            errors.AppendLine("Errors");
            var success = new StringBuilder();

            success.AppendLine("Exported Private Key (Hex); Unix Time UTC; IsChange; Address; Label:");
            int errorCount   = 0;
            int successCount = 0;

            try
            {
                header.AppendLine($"{addresses.Count} found in wallet.");

                var enc = new Bech32Encoder($"{C.Network.CoinTicker.ToLowerInvariant()}key");

                foreach (var a in addresses)
                {
                    try
                    {
                        var decryptedKey = VCL.DecryptWithPassphrase(exportKeysRequest.WalletPassphrase, a.GetEncryptedPrivateKey());
                        if (decryptedKey == null)
                        {
                            errorCount++;
                            header.AppendLine(
                                $"Address '{a.Address}'  could not be decrpted with this passphrase.");
                        }
                        else
                        {
                            var privateKey = enc.Encode(0, decryptedKey);
                            success.AppendLine($"{privateKey};{a.Address}");
                            successCount++;
                        }
                    }
                    catch (Exception e)
                    {
                        header.AppendLine($"Exception processing Address '{a.Address}': {e.Message}");
                    }
                }

                header.AppendLine($"{errorCount} errors occured.");
                header.AppendLine($"{successCount} addresses with private keys successfully exported.");
            }
            catch (Exception e)
            {
                errors.AppendLine(e.Message);
                return(new ExportKeysResponse {
                    Message = $"Export failed because an exception occured: {e.Message}"
                });
            }

            return(new ExportKeysResponse
            {
                Message = $"{header}{Environment.NewLine}{success}{Environment.NewLine}{errors}{Environment.NewLine}"
            });
        }
Exemple #3
0
        public static KeyMaterial CreateHdKeyMaterial(byte[] seed, string keyEncryptionPassphrase, int coinType,
                                                      AddressType addressType, int?isChangeOrInternal, int addressIndex)
        {
            CheckBytes(seed, 64);

            if (string.IsNullOrWhiteSpace(keyEncryptionPassphrase))
            {
                throw new ArgumentException(nameof(keyEncryptionPassphrase));
            }

            var privateExtKey   = CreateDerivedPrivateKey(seed, addressIndex, addressType, isChangeOrInternal, coinType, out string keyDerivationPath);
            var privateKeyBytes = privateExtKey.ToBytes();

            CheckBytes(privateKeyBytes, 32);

            var keyMaterial = new KeyMaterial
            {
                EncryptedPrivateKey = VCL.EncryptWithPassphrase(keyEncryptionPassphrase, privateKeyBytes),
                IsChange            = isChangeOrInternal,
                AddressIndex        = addressIndex,
                CreatedUtc          = DateTime.UtcNow.ToUnixTime(),
                KeyPath             = keyDerivationPath,
                KeyType             = KeyType.Hd
            };

            return(keyMaterial);
        }
        protected static ECCModel CreateError(Exception e, RequestObject request)
        {
            if (AuthKey == null)
            {
                throw new X1WalletException(HttpStatusCode.NoContent, "Please retry later.", null);
            }

            var responseObject = new ResponseObject <object>();

            if (e is X1WalletException se)
            {
                responseObject.Status     = (int)se.HttpStatusCode;
                responseObject.StatusText = se.Message;
            }
            else
            {
                responseObject.Status     = 500;
                responseObject.StatusText = $"Error: {e.Message}";
            }
            var      responseJson      = Serialize(responseObject);
            var      responseJsonBytes = responseJson.ToUTF8Bytes();
            var      cipherV2Bytes     = VCL.Encrypt(responseJsonBytes, request.CurrentPublicKey.FromBase64(), VCL.ECKeyPair.PrivateKey, AuthKey.PrivateKey);
            ECCModel eccModel          = new ECCModel
            {
                CurrentPublicKey = VCL.ECKeyPair.PublicKey.ToHexString(),
                CipherV2Bytes    = cipherV2Bytes.ToHexString(),
                AuthKey          = AuthKey.PublicKey.ToHexString()
            };

            return(eccModel);
        }
Exemple #5
0
 public static KeyMaterial GenerateRandomKeyMaterial(string keyEncryptionPassphrase, int length)
 {
     return(new KeyMaterial
     {
         EncryptedPrivateKey = VCL.EncryptWithPassphrase(keyEncryptionPassphrase, GetRandom(length)),
         KeyPath = null,
         AddressIndex = null,
         IsChange = null,
         CreatedUtc = DateTime.UtcNow.ToUnixTime(),
         KeyType = KeyType.Generated
     });
 }
Exemple #6
0
        internal static void EnsureDefaultMultisigAddress(string passphrase, X1WalletFile x1WalletFile)
        {
            //if (x1WalletFile.ColdStakingAddresses.Count > 0)
            //    return;

            var decryptedSeed = VCL.DecryptWithPassphrase(passphrase, x1WalletFile.HdSeed);

            KeyMaterial myKeyMaterial = KeyHelper.CreateHdKeyMaterial(decryptedSeed, passphrase, C.Network.Consensus.CoinType, AddressType.MultiSig, C.External, 0);
            PubKey      myPubKey      = myKeyMaterial.GetKey(passphrase).PubKey.Compress();

            // other Key
            KeyMaterial otherKeyMaterial = KeyHelper.CreateHdKeyMaterial(decryptedSeed, passphrase, C.Network.Consensus.CoinType, AddressType.MultiSig, C.External, 1);
            var         otherPubKey      = otherKeyMaterial.GetKey(passphrase).PubKey.Compress();

            // The redeem script looks like:
            // 1 03fad6426522dbda5c5a9f8cab24a54ccc374517ad8790bf7e5a14308afc1bf77b 0340ecf2e20978075a49369e35269ecf0651d2f48061ebbf918f3eb1964051f65c 2 OP_CHECKMULTISIG
            Script redeemScript = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(1, myPubKey, otherPubKey);

            // The address looks like:
            // odx1qvar8r29r8llzj53q5utmcewpju59263h38250ws33lp2q45lmalqg5lmdd
            string bech32ScriptAddress = redeemScript.WitHash.GetAddress(C.Network).ToString();

            // In P2SH payments, we refer to the hash of the Redeem Script as the scriptPubKey. It looks like:
            // 0 674671a8a33ffe295220a717bc65c19728556a3789d547ba118fc2a0569fdf7e
            Script scriptPubKey = redeemScript.WitHash.ScriptPubKey;
            var    scp          = scriptPubKey.ToString();


            var multiSigAddress = new MultiSigAddress
            {
                OwnKey             = myKeyMaterial, Address = bech32ScriptAddress, AddressType = AddressType.MultiSig,
                Label              = "Default 1-of-2 MultiSig", MaxSignatures = 2,
                LastSeenHeight     = null,
                SignaturesRequired = 1,
                RedeemScriptHex    = redeemScript.ToBytes().ToHexString(),
                ScriptPubKeyHex    = scriptPubKey.ToBytes().ToHexString(),
                OtherPublicKeys    = new System.Collections.Generic.Dictionary <string, string>(),
            };

            multiSigAddress.OtherPublicKeys.Add(otherPubKey.ToBytes().ToHexString(), "Bob");



            x1WalletFile.MultiSigAddresses[multiSigAddress.Address] = multiSigAddress;
        }
Exemple #7
0
        internal static void EnsureDefaultColdStakingAddress(string passphrase, X1WalletFile x1WalletFile)
        {
            //if (x1WalletFile.ColdStakingAddresses.Count > 0)
            //    return;

            var decryptedSeed = VCL.DecryptWithPassphrase(passphrase, x1WalletFile.HdSeed);

            var coldKey = KeyHelper.CreateHdKeyMaterial(decryptedSeed, passphrase, C.Network.Consensus.CoinType, AddressType.ColdStakingCold,
                                                        C.External, 0);

            var hotKey = KeyHelper.CreateHdKeyMaterial(decryptedSeed, passphrase, C.Network.Consensus.CoinType, AddressType.ColdStakingHot,
                                                       C.External, 0);

            PubKey coldPubKey = coldKey.GetKey(passphrase).PubKey.Compress();

            Key    hotPrivateKey = hotKey.GetKey(passphrase);
            PubKey hotPubKey     = hotPrivateKey.PubKey.Compress();

            Script csRedeemScript = ColdStakingScriptTemplate.Instance.GenerateScriptPubKey(hotPubKey.WitHash.AsKeyId(), coldPubKey.WitHash.AsKeyId());

            string csScriptAddress = csRedeemScript.WitHash.GetAddress(C.Network).ToString();

            Script csScriptAddressScriptPubKey = csRedeemScript.WitHash.ScriptPubKey;

            var csAddress = new ColdStakingAddress
            {
                ColdKey         = coldKey,
                HotKey          = hotKey,
                StakingKey      = hotPrivateKey.ToBytes(),
                Label           = "Default CS",
                AddressType     = AddressType.ColdStakingHot, // TODO: which address type?
                Address         = csScriptAddress,
                LastSeenHeight  = null,
                ScriptPubKeyHex = csScriptAddressScriptPubKey.ToBytes().ToHexString(),
                RedeemScriptHex = csRedeemScript.ToBytes().ToHexString()
            };

            if (csAddress.AddressType == AddressType.ColdStakingHot)
            {
                csAddress.StakingKey = hotPrivateKey.ToBytes();
            }

            x1WalletFile.ColdStakingAddresses[csAddress.Address] = csAddress;
        }
Exemple #8
0
        public static KeyMaterial ImportKeyMaterial(byte[] privateKey, string keyEncryptionPassphrase)
        {
            CheckBytes(privateKey, 32);

            if (string.IsNullOrWhiteSpace(keyEncryptionPassphrase))
            {
                throw new ArgumentException(nameof(keyEncryptionPassphrase));
            }

            return(new KeyMaterial
            {
                EncryptedPrivateKey = VCL.EncryptWithPassphrase(keyEncryptionPassphrase, privateKey),
                KeyPath = null,
                AddressIndex = null,
                IsChange = null,
                CreatedUtc = DateTime.UtcNow.ToUnixTime(),
                KeyType = KeyType.Imported
            });
        }
Exemple #9
0
        /// <summary>
        /// Wallet must be in perfect state, must be called locked.
        /// </summary>
        public static PubKeyHashAddress CreateAndInsertNewReceiveAddress(string label, string passphrase, X1WalletFile x1WalletFile)
        {
            if (!IsLabelUnique(label, x1WalletFile))
            {
                throw new X1WalletException($"The label '{label}' is already in use");
            }

            var nextIndex = GetNextIndex(C.External, x1WalletFile, out var _);

            var decryptedSeed = VCL.DecryptWithPassphrase(passphrase, x1WalletFile.HdSeed);
            var address       = GeneratePubKeyHashAddress(decryptedSeed, passphrase, x1WalletFile.CoinType, C.External, nextIndex);

            if (!x1WalletFile.PubKeyHashAddresses.TryAdd(address.Address, address))
            {
                throw new X1WalletException(HttpStatusCode.InternalServerError, $"Receive address {address.Address} already exists.");
            }

            x1WalletFile.SaveX1WalletFile(x1WalletFile.CurrentPath);
            return(address);
        }
        protected static DecryptedRequest DecryptRequest(RequestObject request, WalletController walletController)
        {
            byte[] decrypted = VCL.Decrypt(request.CipherV2Bytes.FromBase64(), request.CurrentPublicKey.FromBase64(), VCL.ECKeyPair.PrivateKey, AuthKey.PrivateKey);
            if (decrypted == null)
            {
                throw new X1WalletException((HttpStatusCode)427, "Public key changed - please reload", null);
            }
            string           json             = decrypted.FromUTF8Bytes();
            DecryptedRequest decryptedRequest = JsonConvert.DeserializeObject <DecryptedRequest>(json);

            if (((IList)CommandsWithoutWalletNameCheck).Contains(decryptedRequest.Command))
            {
                return(decryptedRequest);
            }
            if (decryptedRequest.Target == null)
            {
                throw new X1WalletException(HttpStatusCode.BadRequest, "No wallet name was supplied.");
            }
            walletController.SetWalletName(decryptedRequest.Target.Replace($".{walletController.CoinTicker}{X1WalletFile.FileExtension}", ""));
            return(decryptedRequest);
        }
        public override Task InitializeAsync()
        {
            var authKeyFile = Path.Combine(this.dataFolder.RootPath, ServerAuthKeyFilename);

            if (File.Exists(authKeyFile))
            {
                var       existing        = File.ReadAllText(authKeyFile);
                ECKeyPair existingAuthKey = JsonConvert.DeserializeObject <ECKeyPair>(existing);
                SecureApiControllerBase.AuthKey = existingAuthKey;
                this.logger.LogInformation($"Using existing authentication key from {authKeyFile}.");
                return(Task.CompletedTask);
            }

            ECKeyPair newAuthKey = VCL.Instance().GenerateECKeyPair().Result;
            var       created    = JsonConvert.SerializeObject(newAuthKey);

            File.WriteAllText(authKeyFile, created);
            SecureApiControllerBase.AuthKey = newAuthKey;
            this.logger.LogInformation($"Created authentication key in {authKeyFile}.");
            return(Task.CompletedTask);
        }
        protected static ECCModel CreateOk <T>(T data, RequestObject request)
        {
            if (AuthKey == null)
            {
                throw new X1WalletException(HttpStatusCode.NoContent, "Please retry later.", null);
            }

            var responseObject = new ResponseObject <T> {
                ResponsePayload = data, Status = 200, StatusText = "OK"
            };
            var      responseJson      = Serialize(responseObject);
            var      responseJsonBytes = responseJson.ToUTF8Bytes();
            var      cipherV2Bytes     = VCL.Encrypt(responseJsonBytes, request.CurrentPublicKey.FromBase64(), VCL.ECKeyPair.PrivateKey, AuthKey.PrivateKey);
            ECCModel eccModel          = new ECCModel
            {
                CurrentPublicKey = VCL.ECKeyPair.PublicKey.ToHexString(),
                CipherV2Bytes    = cipherV2Bytes.ToHexString(),
                AuthKey          = AuthKey.PublicKey.ToHexString()
            };

            return(eccModel);
        }
Exemple #13
0
        public void StartStaking(string passphrase)
        {
            if (this.isDisposing)
            {
                return;
            }

            Guard.NotNull(passphrase, nameof(passphrase));

            if (VCL.DecryptWithPassphrase(passphrase, base.GetPassphraseChallenge()) == null)
            {
                throw new X1WalletException(HttpStatusCode.Unauthorized, "The passphrase is not correct.");
            }

            base.SetStakingPassphrase(passphrase);


            if (this.stakingService == null)
            {
                this.stakingService = new StakingService(this, passphrase, this.nodeServices);
                this.stakingService.Start();
            }
        }
Exemple #14
0
        /// <summary>
        /// We assume that all PubKeyHashAddress addresses in this file are already used. Therefore this method
        /// should only be called when the wallet is fully up-to-date so that all used addresses are already discovered.
        /// </summary>
        public static List <PubKeyHashAddress> CreateAndInsertNewChangeAddresses(string passphrase, int addressesToCreate, X1WalletFile x1WalletFile)
        {
            var decryptedSeed = VCL.DecryptWithPassphrase(passphrase, x1WalletFile.HdSeed);

            var nextIndex = GetNextIndex(C.Change, x1WalletFile, out var _);

            var created      = 0;
            var newAddresses = new List <PubKeyHashAddress>(addressesToCreate);

            while (created < addressesToCreate)
            {
                var address = GeneratePubKeyHashAddress(decryptedSeed, passphrase, x1WalletFile.CoinType, C.Change, nextIndex);
                newAddresses.Add(address);
                if (!x1WalletFile.PubKeyHashAddresses.TryAdd(address.Address, address))
                {
                    Log.Logger.LogWarning($"Change address {address.Address} already existed - nothing was added.");
                }
                created++;
                nextIndex++;
            }

            x1WalletFile.SaveX1WalletFile(x1WalletFile.CurrentPath);
            return(newAddresses);
        }
Exemple #15
0
 public static Key GetPrivateKey(this SegWitCoin segWitCoin, string passphrase)
 {
     return(new Key(VCL.DecryptWithPassphrase(passphrase, segWitCoin.SegWitAddress.GetEncryptedPrivateKey())));
 }
Exemple #16
0
        public void CreateWallet(WalletCreateRequest walletCreateRequest)
        {
            string walletName = walletCreateRequest.WalletName;
            string filePath   = walletName.GetX1WalletFilepath(C.Network, this.nodeServices.DataFolder);
            string passphrase = walletCreateRequest.Passphrase;

            if (File.Exists(filePath))
            {
                throw new InvalidOperationException(
                          $"A wallet with the name {walletName} already exists at {filePath}!");
            }

            if (string.IsNullOrWhiteSpace(passphrase))
            {
                throw new InvalidOperationException("A passphrase is required.");
            }

            DateTime now = DateTime.UtcNow;

            var x1WalletFile = new X1WalletFile
            {
                WalletGuid          = Guid.NewGuid(),
                WalletName          = walletName,
                CoinTicker          = C.Network.CoinTicker,
                CoinType            = C.Network.Consensus.CoinType,
                CreatedUtc          = now.ToUnixTime(),
                ModifiedUtc         = now.ToUnixTime(),
                LastBackupUtc       = null,
                Comment             = "Your notes here!",
                Version             = C.WalletKeyFileVersion,
                PassphraseChallenge = KeyHelper.GenerateRandomKeyMaterial(passphrase, 32)
                                      .EncryptedPrivateKey,
                CurrentPath = filePath
            };


            byte[] mnemonicBytes   = KeyHelper.GetRandom(32);
            string bip39Passphrase = walletCreateRequest.Bip39Passphrase?.Trim() ?? "";

            Wordlist wl       = Wordlist.English;
            var      mnemonic = new Mnemonic(wl, mnemonicBytes);

            byte[] hdSeed = mnemonic.DeriveSeed(bip39Passphrase);

            x1WalletFile.HdSeed = VCL.EncryptWithPassphrase(passphrase, hdSeed);
            x1WalletFile.HdSeedHasBip39Passphrase = !string.IsNullOrWhiteSpace(bip39Passphrase);

            // Create one receive addresses, so that GetUsedReceiveAddresses returns at least one address, even if it is not used in this case.
            AddressService.CreateAndInsertNewReceiveAddress("Default address", passphrase, x1WalletFile);

            AddressService.CreateAndInsertNewChangeAddresses(passphrase, C.UnusedChangeAddressBuffer, x1WalletFile);

            ColdStakingAddressService.EnsureDefaultColdStakingAddress(passphrase, x1WalletFile);
            MultiSigAddressService.EnsureDefaultMultisigAddress(passphrase, x1WalletFile);


            AddressService.TryUpdateLookAhead(passphrase, x1WalletFile);

            x1WalletFile.SaveX1WalletFile(filePath);

            X1WalletMetadataFile x1WalletMetadataFile =
                x1WalletFile.CreateX1WalletMetadataFile(C.Network.GenesisHash);
            string x1WalletMetadataFilename = walletName.GetX1WalletMetaDataFilepath(C.Network, this.nodeServices.DataFolder);

            x1WalletMetadataFile.SaveX1WalletMetadataFile(x1WalletMetadataFilename);
        }
Exemple #17
0
 public static Key GetKey(this KeyMaterial keyMaterial, string passphrase)
 {
     return(new Key(VCL.DecryptWithPassphrase(passphrase, keyMaterial.EncryptedPrivateKey)));
 }
        public static ImportKeysResponse ImportKeys(ImportKeysRequest importKeysRequest, byte[] passphraseChallenge)
        {
            if (importKeysRequest == null)
            {
                throw new ArgumentNullException(nameof(importKeysRequest));
            }
            if (importKeysRequest.WalletPassphrase == null)
            {
                throw new ArgumentNullException(nameof(importKeysRequest.WalletPassphrase));
            }
            if (importKeysRequest.Keys == null)
            {
                throw new ArgumentNullException(nameof(importKeysRequest.Keys));
            }

            var delimiters = new HashSet <char>();

            foreach (var c in importKeysRequest.Keys.Trim().ToCharArray())
            {
                if (char.IsWhiteSpace(c))
                {
                    delimiters.Add(c);
                }
            }

            var items        = importKeysRequest.Keys.Split(delimiters.ToArray());
            var possibleKeys = items.Where(i => i.Length == 52).Distinct().ToList();

            if (possibleKeys.Count == 0)
            {
                throw new X1WalletException(HttpStatusCode.BadRequest, "Input material cointained no keys.");
            }

            var test = VCL.DecryptWithPassphrase(importKeysRequest.WalletPassphrase, passphraseChallenge);

            if (test == null)
            {
                throw new X1WalletException(HttpStatusCode.Unauthorized,
                                            "Your passphrase is incorrect.");
            }
            var importedAddresses = new List <string>();

            var obsidianNetwork = new ObsidianNetwork();

            foreach (var candidate in possibleKeys)
            {
                try
                {
                    var secret     = new BitcoinSecret(candidate, obsidianNetwork);
                    var privateKey = secret.PrivateKey.ToBytes();
                    throw new NotImplementedException();
                    //var address = AddressHelper.CreateWithPrivateKey(privateKey, importKeysRequest.WalletPassphrase, AddressType.SingleKey);

                    //this.X1WalletFile.Addresses.Add(address.Address, address);
                    //importedAddresses.Add($"{secret.GetAddress()} -> {address.Address}");
                }
                catch (Exception e)
                {
                    Log.Logger.LogWarning($"Could not import '{candidate}' as key or address. {e.Message}");
                }
            }

            // this.X1WalletFile.SaveX1WalletFile(this.CurrentX1WalletFilePath);

            var response = new ImportKeysResponse
            {
                ImportedAddresses = importedAddresses, Message = $"Imported {importedAddresses.Count} addresses."
            };

            return(response);
        }