Ejemplo n.º 1
0
 private static void ThrowIfError(string responseString, IEnumerable <HwiOption> options)
 {
     if (HwiParser.TryParseErrors(responseString, options, out HwiException error))
     {
         throw error;
     }
 }
Ejemplo n.º 2
0
        public void TryParseVersionTests(string input, bool isParsable)
        {
            Version expectedVersion = new(1, 1, 2);

            Assert.Equal(isParsable, HwiParser.TryParseVersion(input, out Version? actualVersion));

            if (isParsable)
            {
                Assert.Equal(expectedVersion, actualVersion);
            }
        }
Ejemplo n.º 3
0
        private async Task <string> SendCommandAsync(IEnumerable <HwiOption> options, HwiCommands?command, string commandArguments, bool openConsole, CancellationToken cancel, bool isRecursion = false)
        {
            string arguments = HwiParser.ToArgumentString(Network, options, command, commandArguments);

            try
            {
                (string responseString, int exitCode) = await Bridge.SendCommandAsync(arguments, openConsole, cancel).ConfigureAwait(false);

                ThrowIfError(responseString, options, arguments, exitCode);

                return(responseString);
            }
            catch (Exception ex) when(ex is OperationCanceledException || ex is TaskCanceledException || ex is TimeoutException)
            {
                throw new OperationCanceledException($"'hwi {arguments}' operation is canceled.", ex);
            }
            //// HWI is inconsistent with error codes here.
            catch (HwiException ex) when(ex.ErrorCode == HwiErrorCode.DeviceConnError || ex.ErrorCode == HwiErrorCode.DeviceNotReady)
            {
                // Probably didn't find device with specified fingerprint.
                // Enumerate and call again, but not forever.
                if (isRecursion || !options.Any(x => x.Type == HwiOptions.Fingerprint))
                {
                    throw;
                }

                IEnumerable <HwiEnumerateEntry> hwiEntries = await EnumerateAsync(cancel, isRecursion : true);

                // Trezor T won't give Fingerprint info so we'll assume that the first device that doesn't give fingerprint is what we need.
                HwiEnumerateEntry firstNoFingerprintEntry = hwiEntries.Where(x => x.Fingerprint is null).FirstOrDefault();

                if (firstNoFingerprintEntry is null)
                {
                    throw;
                }

                // Build options without fingerprint with device model and device path.
                var newOptions = BuildOptions(firstNoFingerprintEntry.Model, firstNoFingerprintEntry.Path, fingerprint: null, options.Where(x => x.Type != HwiOptions.Fingerprint).ToArray());

                return(await SendCommandAsync(newOptions, command, arguments, openConsole, cancel, isRecursion : true));
            }
            catch (HwiException ex) when(Network != Network.Main && ex.ErrorCode == HwiErrorCode.UnknownError && ex.Message?.Contains("DataError: Forbidden key path") is true)
            {
                // Trezor only accepts KeyPath 84'/1' on TestNet from v2.3.1. We fake that we are on MainNet to ensure compatibility.
                string fixedArguments = HwiParser.ToArgumentString(Network.Main, options, command, commandArguments);

                (string responseString, int exitCode) = await Bridge.SendCommandAsync(fixedArguments, openConsole, cancel).ConfigureAwait(false);

                ThrowIfError(responseString, options, fixedArguments, exitCode);

                return(responseString);
            }
        }
Ejemplo n.º 4
0
        public async Task <Version> GetVersionAsync(CancellationToken cancel)
        {
            string responseString = await SendCommandAsync(
                options : new[] { HwiOption.Version },
                command : null,
                commandArguments : null,
                openConsole : false,
                cancel).ConfigureAwait(false);

            var version = HwiParser.ParseVersion(responseString);

            return(version);
        }
Ejemplo n.º 5
0
        public async Task <IEnumerable <HwiEnumerateEntry> > EnumerateAsync(CancellationToken cancel, bool isRecursion = false)
        {
            string responseString = await SendCommandAsync(
                options : null,
                command : HwiCommands.Enumerate,
                commandArguments : null,
                openConsole : false,
                cancel,
                isRecursion).ConfigureAwait(false);

            IEnumerable <HwiEnumerateEntry> response = HwiParser.ParseHwiEnumerateResponse(responseString);

            return(response);
        }
Ejemplo n.º 6
0
        private async Task <ExtPubKey> GetXpubImplAsync(HardwareWalletModels?deviceType, string devicePath, HDFingerprint?fingerprint, KeyPath keyPath, CancellationToken cancel)
        {
            string keyPathString = keyPath.ToString(true, "h");
            var    response      = await SendCommandAsync(
                options : BuildOptions(deviceType, devicePath, fingerprint),
                command : HwiCommands.GetXpub,
                commandArguments : keyPathString,
                openConsole : false,
                cancel).ConfigureAwait(false);

            var extPubKey = HwiParser.ParseExtPubKey(response);

            return(extPubKey);
        }
Ejemplo n.º 7
0
        private async Task <BitcoinWitPubKeyAddress> DisplayAddressImplAsync(HardwareWalletModels?deviceType, string devicePath, HDFingerprint?fingerprint, KeyPath keyPath, CancellationToken cancel)
        {
            var response = await SendCommandAsync(
                options : BuildOptions(deviceType, devicePath, fingerprint),
                command : HwiCommands.DisplayAddress,
                commandArguments : $"--path {keyPath.ToString(true, "h")} --wpkh",
                openConsole : false,
                cancel).ConfigureAwait(false);

            var address = HwiParser.ParseAddress(response, Network) as BitcoinWitPubKeyAddress;

            address = address.TransformToNetworkNetwork(Network);

            return(address);
        }
Ejemplo n.º 8
0
        private static void ThrowIfError(string responseString, IEnumerable <HwiOption> options, string arguments, int exitCode)
        {
            if (exitCode != 0)
            {
                if (HwiParser.TryParseErrors(responseString, options, out HwiException error))
                {
                    throw error;
                }
                throw new HwiException(HwiErrorCode.UnknownError, $"'hwi {arguments}' exited with incorrect exit code: {exitCode}.");
            }

            if (HwiParser.TryParseErrors(responseString, options, out HwiException error2))
            {
                throw error2;
            }
        }
Ejemplo n.º 9
0
        private async Task <PSBT> SignTxImplAsync(HardwareWalletModels?deviceType, string devicePath, HDFingerprint?fingerprint, PSBT psbt, CancellationToken cancel)
        {
            var psbtString = psbt.ToBase64();

            var response = await SendCommandAsync(
                options : BuildOptions(deviceType, devicePath, fingerprint),
                command : HwiCommands.SignTx,
                commandArguments : psbtString,
                openConsole : false,
                cancel).ConfigureAwait(false);

            PSBT signedPsbt = HwiParser.ParsePsbt(response, Network);

            if (!signedPsbt.IsAllFinalized())
            {
                signedPsbt.Finalize();
            }

            return(signedPsbt);
        }
Ejemplo n.º 10
0
        public async Task ColdCardMk1MockTestsAsync(Network network)
        {
            var client = new HwiClient(network, new HwiProcessBridgeMock(HardwareWalletModels.Coldcard));

            using var cts = new CancellationTokenSource(ReasonableRequestTimeout);
            IEnumerable <HwiEnumerateEntry> enumerate = await client.EnumerateAsync(cts.Token);

            Assert.Single(enumerate);
            HwiEnumerateEntry entry = enumerate.Single();

            Assert.Equal(HardwareWalletModels.Coldcard, entry.Model);
            string rawPath        = "\\\\\\\\?\\\\hid#vid_d13e&pid_cc10&mi_00#7&1b239988&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}";
            string normalizedPath = HwiParser.NormalizeRawDevicePath(rawPath);

            Assert.Equal(normalizedPath, entry.Path);
            Assert.Null(entry.NeedsPassphraseSent);
            Assert.Null(entry.NeedsPinSent);
            Assert.Null(entry.Error);
            Assert.Null(entry.Code);
            Assert.Equal("a3d0d797", entry.Fingerprint.ToString());
            Assert.True(entry.IsInitialized());

            var           deviceType  = entry.Model;
            var           devicePath  = entry.Path;
            HDFingerprint fingerprint = entry.Fingerprint.Value;

            var wipe = await Assert.ThrowsAsync <HwiException>(async() => await client.WipeAsync(deviceType, devicePath, cts.Token));

            Assert.Equal("The Coldcard does not support wiping via software", wipe.Message);
            Assert.Equal(HwiErrorCode.UnavailableAction, wipe.ErrorCode);

            // ColdCard doesn't support it.
            var setup = await Assert.ThrowsAsync <HwiException>(async() => await client.SetupAsync(deviceType, devicePath, false, cts.Token));

            Assert.Equal("The Coldcard does not support software setup", setup.Message);
            Assert.Equal(HwiErrorCode.UnavailableAction, setup.ErrorCode);

            // ColdCard doesn't support it.
            var restore = await Assert.ThrowsAsync <HwiException>(async() => await client.RestoreAsync(deviceType, devicePath, false, cts.Token));

            Assert.Equal("The Coldcard does not support restoring via software", restore.Message);
            Assert.Equal(HwiErrorCode.UnavailableAction, restore.ErrorCode);

            // ColdCard doesn't support it.
            var promptpin = await Assert.ThrowsAsync <HwiException>(async() => await client.PromptPinAsync(deviceType, devicePath, cts.Token));

            Assert.Equal("The Coldcard does not need a PIN sent from the host", promptpin.Message);
            Assert.Equal(HwiErrorCode.UnavailableAction, promptpin.ErrorCode);

            // ColdCard doesn't support it.
            var sendpin = await Assert.ThrowsAsync <HwiException>(async() => await client.SendPinAsync(deviceType, devicePath, 1111, cts.Token));

            Assert.Equal("The Coldcard does not need a PIN sent from the host", sendpin.Message);
            Assert.Equal(HwiErrorCode.UnavailableAction, sendpin.ErrorCode);

            KeyPath   keyPath1 = KeyManager.DefaultAccountKeyPath;
            KeyPath   keyPath2 = KeyManager.DefaultAccountKeyPath.Derive(1);
            ExtPubKey xpub1    = await client.GetXpubAsync(deviceType, devicePath, keyPath1, cts.Token);

            ExtPubKey xpub2 = await client.GetXpubAsync(deviceType, devicePath, keyPath2, cts.Token);

            var expecteXpub1 = NBitcoinHelpers.BetterParseExtPubKey("xpub6DHjDx4gzLV37gJWMxYJAqyKRGN46MT61RHVizdU62cbVUYu9L95cXKzX62yJ2hPbN11EeprS8sSn8kj47skQBrmycCMzFEYBQSntVKFQ5M");
            var expecteXpub2 = NBitcoinHelpers.BetterParseExtPubKey("xpub6FJS1ne3STcKdQ9JLXNzZXidmCNZ9dxLiy7WVvsRkcmxjJsrDKJKEAXq4MGyEBM3vHEw2buqXezfNK5SNBrkwK7Fxjz1TW6xzRr2pUyMWFu");

            Assert.Equal(expecteXpub1, xpub1);
            Assert.Equal(expecteXpub2, xpub2);

            BitcoinWitPubKeyAddress address1 = await client.DisplayAddressAsync(deviceType, devicePath, keyPath1, cts.Token);

            BitcoinWitPubKeyAddress address2 = await client.DisplayAddressAsync(deviceType, devicePath, keyPath2, cts.Token);

            BitcoinAddress expectedAddress1;
            BitcoinAddress expectedAddress2;

            if (network == Network.Main)
            {
                expectedAddress1 = BitcoinAddress.Create("bc1q7zqqsmqx5ymhd7qn73lm96w5yqdkrmx7fdevah", Network.Main);
                expectedAddress2 = BitcoinAddress.Create("bc1qmaveee425a5xjkjcv7m6d4gth45jvtnj23fzyf", Network.Main);
            }
            else if (network == Network.TestNet)
            {
                expectedAddress1 = BitcoinAddress.Create("tb1q7zqqsmqx5ymhd7qn73lm96w5yqdkrmx7rtzlxy", Network.TestNet);
                expectedAddress2 = BitcoinAddress.Create("tb1qmaveee425a5xjkjcv7m6d4gth45jvtnjqhj3l6", Network.TestNet);
            }
            else if (network == Network.RegTest)
            {
                expectedAddress1 = BitcoinAddress.Create("bcrt1q7zqqsmqx5ymhd7qn73lm96w5yqdkrmx7pzmj3d", Network.RegTest);
                expectedAddress2 = BitcoinAddress.Create("bcrt1qmaveee425a5xjkjcv7m6d4gth45jvtnjz7tugn", Network.RegTest);
            }
            else
            {
                throw new NotSupportedNetworkException(network);
            }

            Assert.Equal(expectedAddress1, address1);
            Assert.Equal(expectedAddress2, address2);
        }
Ejemplo n.º 11
0
        public async Task TrezorOneMockTestsAsync(Network network)
        {
            var client = new HwiClient(network, new HwiProcessBridgeMock(HardwareWalletModels.Trezor_1));

            using var cts = new CancellationTokenSource(ReasonableRequestTimeout);
            IEnumerable <HwiEnumerateEntry> enumerate = await client.EnumerateAsync(cts.Token);

            Assert.Single(enumerate);
            HwiEnumerateEntry entry = enumerate.Single();

            Assert.Equal(HardwareWalletModels.Trezor_1, entry.Model);
            string rawPath        = "hid:\\\\\\\\?\\\\hid#vid_534c&pid_0001&mi_00#7&6f0b727&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}";
            string normalizedPath = HwiParser.NormalizeRawDevicePath(rawPath);

            Assert.Equal(normalizedPath, entry.Path);
            Assert.False(entry.NeedsPassphraseSent);
            Assert.True(entry.NeedsPinSent);
            Assert.Equal("Could not open client or get fingerprint information: Trezor is locked. Unlock by using 'promptpin' and then 'sendpin'.", entry.Error);
            Assert.Equal(HwiErrorCode.DeviceNotReady, entry.Code);
            Assert.Null(entry.Fingerprint);
            Assert.True(entry.IsInitialized());

            var deviceType = entry.Model;
            var devicePath = entry.Path;

            await client.WipeAsync(deviceType, devicePath, cts.Token);

            await client.SetupAsync(deviceType, devicePath, false, cts.Token);

            await client.RestoreAsync(deviceType, devicePath, false, cts.Token);

            var promptpin = await Assert.ThrowsAsync <HwiException>(async() => await client.PromptPinAsync(deviceType, devicePath, cts.Token));

            Assert.Equal("The PIN has already been sent to this device", promptpin.Message);
            Assert.Equal(HwiErrorCode.DeviceAlreadyUnlocked, promptpin.ErrorCode);

            var sendpin = await Assert.ThrowsAsync <HwiException>(async() => await client.SendPinAsync(deviceType, devicePath, 1111, cts.Token));

            Assert.Equal("The PIN has already been sent to this device", sendpin.Message);
            Assert.Equal(HwiErrorCode.DeviceAlreadyUnlocked, sendpin.ErrorCode);

            KeyPath   keyPath1 = KeyManager.DefaultAccountKeyPath;
            KeyPath   keyPath2 = KeyManager.DefaultAccountKeyPath.Derive(1);
            ExtPubKey xpub1    = await client.GetXpubAsync(deviceType, devicePath, keyPath1, cts.Token);

            ExtPubKey xpub2 = await client.GetXpubAsync(deviceType, devicePath, keyPath2, cts.Token);

            var expecteXpub1 = NBitcoinHelpers.BetterParseExtPubKey("xpub6DHjDx4gzLV37gJWMxYJAqyKRGN46MT61RHVizdU62cbVUYu9L95cXKzX62yJ2hPbN11EeprS8sSn8kj47skQBrmycCMzFEYBQSntVKFQ5M");
            var expecteXpub2 = NBitcoinHelpers.BetterParseExtPubKey("xpub6FJS1ne3STcKdQ9JLXNzZXidmCNZ9dxLiy7WVvsRkcmxjJsrDKJKEAXq4MGyEBM3vHEw2buqXezfNK5SNBrkwK7Fxjz1TW6xzRr2pUyMWFu");

            Assert.Equal(expecteXpub1, xpub1);
            Assert.Equal(expecteXpub2, xpub2);

            BitcoinWitPubKeyAddress address1 = await client.DisplayAddressAsync(deviceType, devicePath, keyPath1, cts.Token);

            BitcoinWitPubKeyAddress address2 = await client.DisplayAddressAsync(deviceType, devicePath, keyPath2, cts.Token);

            BitcoinAddress expectedAddress1;
            BitcoinAddress expectedAddress2;

            if (network == Network.Main)
            {
                expectedAddress1 = BitcoinAddress.Create("bc1q7zqqsmqx5ymhd7qn73lm96w5yqdkrmx7fdevah", Network.Main);
                expectedAddress2 = BitcoinAddress.Create("bc1qmaveee425a5xjkjcv7m6d4gth45jvtnj23fzyf", Network.Main);
            }
            else if (network == Network.TestNet)
            {
                expectedAddress1 = BitcoinAddress.Create("tb1q7zqqsmqx5ymhd7qn73lm96w5yqdkrmx7rtzlxy", Network.TestNet);
                expectedAddress2 = BitcoinAddress.Create("tb1qmaveee425a5xjkjcv7m6d4gth45jvtnjqhj3l6", Network.TestNet);
            }
            else if (network == Network.RegTest)
            {
                expectedAddress1 = BitcoinAddress.Create("bcrt1q7zqqsmqx5ymhd7qn73lm96w5yqdkrmx7pzmj3d", Network.RegTest);
                expectedAddress2 = BitcoinAddress.Create("bcrt1qmaveee425a5xjkjcv7m6d4gth45jvtnjz7tugn", Network.RegTest);
            }
            else
            {
                throw new NotSupportedNetworkException(network);
            }

            Assert.Equal(expectedAddress1, address1);
            Assert.Equal(expectedAddress2, address2);
        }
        public Task <(string response, int exitCode)> SendCommandAsync(string arguments, bool openConsole, CancellationToken cancel, Action <StreamWriter>?standardInputWriter = null)
        {
            if (openConsole)
            {
                throw new NotImplementedException($"Cannot mock {nameof(openConsole)} mode.");
            }

            string model;
            string rawPath;

            model = Model switch
            {
                HardwareWalletModels.Trezor_T => "trezor_t",
                HardwareWalletModels.Trezor_1 => "trezor_1",
                HardwareWalletModels.Coldcard => "coldcard",
                HardwareWalletModels.Ledger_Nano_S => "ledger_nano_s",
                HardwareWalletModels.Ledger_Nano_X => "ledger_nano_x",
                _ => throw new NotImplementedException("Mock missing.")
            };

            rawPath = Model switch
            {
                HardwareWalletModels.Trezor_T => "webusb: 001:4",
                HardwareWalletModels.Trezor_1 => "hid:\\\\\\\\?\\\\hid#vid_534c&pid_0001&mi_00#7&6f0b727&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}",
                HardwareWalletModels.Coldcard => @"\\\\?\\hid#vid_d13e&pid_cc10&mi_00#7&1b239988&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}",
                HardwareWalletModels.Ledger_Nano_S => "\\\\\\\\?\\\\hid#vid_2c97&pid_0001&mi_00#7&e45ae20&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}",
                HardwareWalletModels.Ledger_Nano_X => "\\\\\\\\?\\\\hid#vid_2c97&pid_0001&mi_00#7&e45ae20&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}",
                _ => throw new NotImplementedException("Mock missing.")
            };

            string path = HwiParser.NormalizeRawDevicePath(rawPath);
            string devicePathAndTypeArgumentString = $"--device-path \"{path}\" --device-type \"{model}\"";

            const string SuccessTrueResponse = "{\"success\": true}\r\n";

            string?response = null;
            int    code     = 0;

            if (CompareArguments(arguments, "enumerate"))
            {
                response = Model switch
                {
                    HardwareWalletModels.Trezor_T => $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"needs_pin_sent\": false, \"needs_passphrase_sent\": false, \"error\": \"Not initialized\"}}]",
                    HardwareWalletModels.Trezor_1 => $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"needs_pin_sent\": true, \"needs_passphrase_sent\": false, \"error\": \"Could not open client or get fingerprint information: Trezor is locked. Unlock by using 'promptpin' and then 'sendpin'.\", \"code\": -12}}]\r\n",
                    HardwareWalletModels.Coldcard => $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"needs_passphrase\": false, \"fingerprint\": \"a3d0d797\"}}]\r\n",
                    HardwareWalletModels.Ledger_Nano_S => $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"fingerprint\": \"4054d6f6\", \"needs_pin_sent\": false, \"needs_passphrase_sent\": false}}]\r\n",
                    HardwareWalletModels.Ledger_Nano_X => $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"fingerprint\": \"4054d6f6\", \"needs_pin_sent\": false, \"needs_passphrase_sent\": false}}]\r\n",
                    _ => throw new NotImplementedException($"Mock missing for {model}")
                };
            }
            else if (CompareArguments(arguments, $"{devicePathAndTypeArgumentString} wipe"))
            {
                response = Model switch
                {
                    HardwareWalletModels.Trezor_T or HardwareWalletModels.Trezor_1 => SuccessTrueResponse,
                                                                          HardwareWalletModels.Coldcard => "{\"error\": \"The Coldcard does not support wiping via software\", \"code\": -9}\r\n",
                                                                          HardwareWalletModels.Ledger_Nano_S => "{\"error\": \"The Ledger Nano S does not support wiping via software\", \"code\": -9}\r\n",
                                                                          HardwareWalletModels.Ledger_Nano_X => "{\"error\": \"The Ledger Nano X does not support wiping via software\", \"code\": -9}\r\n",
                                                                          _ => throw new NotImplementedException("Mock missing.")
                };
            }
            else if (CompareArguments(arguments, $"{devicePathAndTypeArgumentString} setup"))
            {
                response = Model switch
                {
                    HardwareWalletModels.Trezor_T or HardwareWalletModels.Trezor_1 => "{\"error\": \"setup requires interactive mode\", \"code\": -9}",
                                                                          HardwareWalletModels.Coldcard => "{\"error\": \"The Coldcard does not support software setup\", \"code\": -9}\r\n",
                                                                          HardwareWalletModels.Ledger_Nano_S => "{\"error\": \"The Ledger Nano S does not support software setup\", \"code\": -9}\r\n",
                                                                          HardwareWalletModels.Ledger_Nano_X => "{\"error\": \"The Ledger Nano X does not support software setup\", \"code\": -9}\r\n",
                                                                          _ => throw new NotImplementedException("Mock missing.")
                };
            }
            else if (CompareArguments(arguments, $"{devicePathAndTypeArgumentString} --interactive setup"))
            {
                response = Model switch
                {
                    HardwareWalletModels.Trezor_T or HardwareWalletModels.Trezor_1 => SuccessTrueResponse,
                                                                          HardwareWalletModels.Coldcard => "{\"error\": \"The Coldcard does not support software setup\", \"code\": -9}\r\n",
                                                                          HardwareWalletModels.Ledger_Nano_S => "{\"error\": \"The Ledger Nano S does not support software setup\", \"code\": -9}\r\n",
                                                                          HardwareWalletModels.Ledger_Nano_X => "{\"error\": \"The Ledger Nano X does not support software setup\", \"code\": -9}\r\n",
                                                                          _ => throw new NotImplementedException("Mock missing.")
                };
            }
            else if (CompareArguments(arguments, $"{devicePathAndTypeArgumentString} --interactive restore"))
            {
                response = Model switch
                {
                    HardwareWalletModels.Trezor_T or HardwareWalletModels.Trezor_1 => SuccessTrueResponse,
                                                                          HardwareWalletModels.Coldcard => "{\"error\": \"The Coldcard does not support restoring via software\", \"code\": -9}\r\n",
                                                                          HardwareWalletModels.Ledger_Nano_S => "{\"error\": \"The Ledger Nano S does not support restoring via software\", \"code\": -9}\r\n",
                                                                          HardwareWalletModels.Ledger_Nano_X => "{\"error\": \"The Ledger Nano X does not support restoring via software\", \"code\": -9}\r\n",
                                                                          _ => throw new NotImplementedException("Mock missing.")
                };
            }
            else if (CompareArguments(arguments, $"{devicePathAndTypeArgumentString} promptpin"))
            {
                response = Model switch
                {
                    HardwareWalletModels.Trezor_T or HardwareWalletModels.Trezor_1 => "{\"error\": \"The PIN has already been sent to this device\", \"code\": -11}",
                                                                          HardwareWalletModels.Coldcard => "{\"error\": \"The Coldcard does not need a PIN sent from the host\", \"code\": -9}\r\n",
                                                                          HardwareWalletModels.Ledger_Nano_S => "{\"error\": \"The Ledger Nano S does not need a PIN sent from the host\", \"code\": -9}\r\n",
                                                                          HardwareWalletModels.Ledger_Nano_X => "{\"error\": \"The Ledger Nano X does not need a PIN sent from the host\", \"code\": -9}\r\n",
                                                                          _ => throw new NotImplementedException("Mock missing.")
                };
            }
            else if (CompareArguments(arguments, $"{devicePathAndTypeArgumentString} sendpin", true))
            {
                response = Model switch
                {
                    HardwareWalletModels.Trezor_T or HardwareWalletModels.Trezor_1 => "{\"error\": \"The PIN has already been sent to this device\", \"code\": -11}",
                                                                          HardwareWalletModels.Coldcard => "{\"error\": \"The Coldcard does not need a PIN sent from the host\", \"code\": -9}\r\n",
                                                                          HardwareWalletModels.Ledger_Nano_S => "{\"error\": \"The Ledger Nano S does not need a PIN sent from the host\", \"code\": -9}\r\n",
                                                                          HardwareWalletModels.Ledger_Nano_X => "{\"error\": \"The Ledger Nano X does not need a PIN sent from the host\", \"code\": -9}\r\n",
                                                                          _ => throw new NotImplementedException("Mock missing.")
                };
            }
            else if (CompareGetXbpubArguments(arguments, out string?xpub))
            {
                if (Model is HardwareWalletModels.Trezor_T or HardwareWalletModels.Coldcard or HardwareWalletModels.Trezor_1 or HardwareWalletModels.Ledger_Nano_S or HardwareWalletModels.Ledger_Nano_X)
                {
                    response = $"{{\"xpub\": \"{xpub}\"}}\r\n";
                }
            }
            else if (CompareArguments(out bool t1, arguments, $"{devicePathAndTypeArgumentString} displayaddress --path m/84h/0h/0h --addr-type wit", false))
            {
                if (Model is HardwareWalletModels.Trezor_T or HardwareWalletModels.Coldcard or HardwareWalletModels.Trezor_1 or HardwareWalletModels.Ledger_Nano_S or HardwareWalletModels.Ledger_Nano_X)
                {
                    response = t1
                                                ? "{\"address\": \"tb1q7zqqsmqx5ymhd7qn73lm96w5yqdkrmx7rtzlxy\"}\r\n"
                                                : "{\"address\": \"bc1q7zqqsmqx5ymhd7qn73lm96w5yqdkrmx7fdevah\"}\r\n";
                }
            }
Ejemplo n.º 13
0
        public Task <(string response, int exitCode)> SendCommandAsync(string arguments, bool openConsole, CancellationToken cancel, Action <StreamWriter>?standardInputWriter = null)
        {
            if (openConsole)
            {
                throw new NotImplementedException($"Cannot mock {nameof(openConsole)} mode.");
            }

            string model;
            string rawPath;

            if (Model == HardwareWalletModels.Trezor_T)
            {
                model   = "trezor_t";
                rawPath = "webusb: 001:4";
            }
            else if (Model == HardwareWalletModels.Trezor_1)
            {
                model   = "trezor_1";
                rawPath = "hid:\\\\\\\\?\\\\hid#vid_534c&pid_0001&mi_00#7&6f0b727&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}";
            }
            else if (Model == HardwareWalletModels.Coldcard)
            {
                model   = "coldcard";
                rawPath = @"\\\\?\\hid#vid_d13e&pid_cc10&mi_00#7&1b239988&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}";
            }
            else if (Model == HardwareWalletModels.Ledger_Nano_S)
            {
                model   = "ledger_nano_s";
                rawPath = "\\\\\\\\?\\\\hid#vid_2c97&pid_0001&mi_00#7&e45ae20&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}";
            }
            else
            {
                throw new NotImplementedException("Mock missing.");
            }

            string path = HwiParser.NormalizeRawDevicePath(rawPath);
            string devicePathAndTypeArgumentString = $"--device-path \"{path}\" --device-type \"{model}\"";

            const string SuccessTrueResponse = "{\"success\": true}\r\n";

            string?response = null;
            int    code     = 0;

            if (CompareArguments(arguments, "enumerate"))
            {
                if (Model == HardwareWalletModels.Trezor_T)
                {
                    response = $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"needs_pin_sent\": false, \"needs_passphrase_sent\": false, \"error\": \"Not initialized\"}}]";
                }
                else if (Model == HardwareWalletModels.Trezor_1)
                {
                    response = $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"needs_pin_sent\": true, \"needs_passphrase_sent\": false, \"error\": \"Could not open client or get fingerprint information: Trezor is locked. Unlock by using 'promptpin' and then 'sendpin'.\", \"code\": -12}}]\r\n";
                }
                else if (Model == HardwareWalletModels.Coldcard)
                {
                    response = $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"needs_passphrase\": false, \"fingerprint\": \"a3d0d797\"}}]\r\n";
                }
                else if (Model == HardwareWalletModels.Ledger_Nano_S)
                {
                    response = $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"fingerprint\": \"4054d6f6\", \"needs_pin_sent\": false, \"needs_passphrase_sent\": false}}]\r\n";
                }
            }
            else if (CompareArguments(arguments, $"{devicePathAndTypeArgumentString} wipe"))
            {
                if (Model is HardwareWalletModels.Trezor_T or HardwareWalletModels.Trezor_1)
                {
                    response = SuccessTrueResponse;
                }
                else if (Model == HardwareWalletModels.Coldcard)
                {
                    response = "{\"error\": \"The Coldcard does not support wiping via software\", \"code\": -9}\r\n";
                }
                else if (Model == HardwareWalletModels.Ledger_Nano_S)
                {
                    response = "{\"error\": \"The Ledger Nano S does not support wiping via software\", \"code\": -9}\r\n";
                }
            }