diff --git a/BTCPayServer/Controllers/VaultController.cs b/BTCPayServer/Controllers/VaultController.cs index b3a694dd1..d4f75f0b8 100644 --- a/BTCPayServer/Controllers/VaultController.cs +++ b/BTCPayServer/Controllers/VaultController.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using BTCPayServer.Data; +using BTCPayServer.Hwi; using BTCPayServer.ModelBinders; using BTCPayServer.Models; using BTCPayServer.Models.StoreViewModels; @@ -60,8 +61,27 @@ namespace BTCPayServer.Controllers Transport = new HwiWebSocketTransport(websocket) }; Hwi.HwiDeviceClient device = null; + HwiEnumerateEntry deviceEntry = null; HDFingerprint? fingerprint = null; + string password = null; + int? pin = null; var websocketHelper = new WebSocketHelper(websocket); + + async Task RequireMoreInformation() + { + if (deviceEntry.NeedsPinSent is true && pin is null) + { + await websocketHelper.Send("{ \"error\": \"need-pin\"}", cancellationToken); + return true; + } + if (deviceEntry.NeedsPassphraseSent is true && password == null) + { + await websocketHelper.Send("{ \"error\": \"need-passphrase\"}", cancellationToken); + return true; + } + return false; + } + JObject o = null; try { @@ -70,12 +90,20 @@ namespace BTCPayServer.Controllers var command = await websocketHelper.NextMessageAsync(cancellationToken); switch (command) { + case "set-passphrase": + device.Password = await websocketHelper.NextMessageAsync(cancellationToken); + password = device.Password; + break; case "ask-sign": if (device == null) { await websocketHelper.Send("{ \"error\": \"need-device\"}", cancellationToken); continue; } + if (await RequireMoreInformation()) + { + continue; + } if (walletId == null) { await websocketHelper.Send("{ \"error\": \"invalid-walletId\"}", cancellationToken); @@ -92,6 +120,11 @@ namespace BTCPayServer.Controllers await websocketHelper.Send("{ \"error\": \"need-pin\"}", cancellationToken); continue; } + catch (Hwi.HwiException ex) when (ex.ErrorCode == Hwi.HwiErrorCode.NoPassword) + { + await websocketHelper.Send("{ \"error\": \"need-passphrase\"}", cancellationToken); + continue; + } } await websocketHelper.Send("{ \"info\": \"ready\"}", cancellationToken); o = JObject.Parse(await websocketHelper.NextMessageAsync(cancellationToken)); @@ -119,6 +152,11 @@ namespace BTCPayServer.Controllers await websocketHelper.Send("{ \"error\": \"need-pin\"}", cancellationToken); continue; } + catch (Hwi.HwiException ex) when (ex.ErrorCode == Hwi.HwiErrorCode.NoPassword) + { + await websocketHelper.Send("{ \"error\": \"need-passphrase\"}", cancellationToken); + continue; + } catch (Hwi.HwiException) { await websocketHelper.Send("{ \"error\": \"user-reject\"}", cancellationToken); @@ -136,11 +174,8 @@ namespace BTCPayServer.Controllers } await device.PromptPinAsync(cancellationToken); await websocketHelper.Send("{ \"info\": \"prompted, please input the pin\"}", cancellationToken); - o = JObject.Parse(await websocketHelper.NextMessageAsync(cancellationToken)); - var pin = (int)o["pinCode"].Value(); - var passphrase = o["passphrase"].Value(); - device.Password = passphrase; - if (await device.SendPinAsync(pin, cancellationToken)) + pin = int.Parse(await websocketHelper.NextMessageAsync(cancellationToken), CultureInfo.InvariantCulture); + if (await device.SendPinAsync(pin.Value, cancellationToken)) { await websocketHelper.Send("{ \"info\": \"the pin is correct\"}", cancellationToken); } @@ -156,6 +191,10 @@ namespace BTCPayServer.Controllers await websocketHelper.Send("{ \"error\": \"need-device\"}", cancellationToken); continue; } + if (await RequireMoreInformation()) + { + continue; + } JObject result = new JObject(); var factory = network.NBXplorerNetwork.DerivationStrategyFactory; var keyPath = new KeyPath("84'").Derive(network.CoinType).Derive(0, true); @@ -169,6 +208,11 @@ namespace BTCPayServer.Controllers await websocketHelper.Send("{ \"error\": \"need-pin\"}", cancellationToken); continue; } + catch (Hwi.HwiException ex) when (ex.ErrorCode == Hwi.HwiErrorCode.NoPassword) + { + await websocketHelper.Send("{ \"error\": \"need-passphrase\"}", cancellationToken); + continue; + } if (fingerprint is null) { fingerprint = (await device.GetXPubAsync(new KeyPath("44'"), cancellationToken)).ExtPubKey.ParentFingerprint; @@ -196,13 +240,18 @@ namespace BTCPayServer.Controllers await websocketHelper.Send(result.ToString(), cancellationToken); break; case "ask-device": - var devices = (await hwi.EnumerateDevicesAsync(cancellationToken)).ToList(); - device = devices.FirstOrDefault(); - if (device == null) + password = null; + pin = null; + deviceEntry = null; + device = null; + var entries = (await hwi.EnumerateEntriesAsync(cancellationToken)).ToList(); + deviceEntry = entries.FirstOrDefault(); + if (deviceEntry == null) { await websocketHelper.Send("{ \"error\": \"no-device\"}", cancellationToken); continue; } + device = new HwiDeviceClient(hwi, deviceEntry.DeviceSelector, deviceEntry.Model, deviceEntry.Fingerprint); fingerprint = device.Fingerprint; JObject json = new JObject(); json.Add("model", device.Model.ToString()); diff --git a/BTCPayServer/Views/Shared/VaultElements.cshtml b/BTCPayServer/Views/Shared/VaultElements.cshtml index d6a7d3c55..33e66ec08 100644 --- a/BTCPayServer/Views/Shared/VaultElements.cshtml +++ b/BTCPayServer/Views/Shared/VaultElements.cshtml @@ -41,7 +41,7 @@
- +
diff --git a/BTCPayServer/wwwroot/js/vaultbridge.ui.js b/BTCPayServer/wwwroot/js/vaultbridge.ui.js index 9f58cebe8..e971bfc3b 100644 --- a/BTCPayServer/wwwroot/js/vaultbridge.ui.js +++ b/BTCPayServer/wwwroot/js/vaultbridge.ui.js @@ -114,6 +114,11 @@ var vaultui = (function () { if (await self.askForPin()) return true; } + if (json.error === "need-passphrase") { + handled = true; + if (await self.askForPassphrase()) + return true; + } if (!handled) { showError(json); } @@ -230,6 +235,15 @@ var vaultui = (function () { }); }; + this.askForPassphrase = async function () { + if (!await self.ensureConnectedToBackend()) + return false; + var passphrase = await self.getUserPassphrase(); + self.bridge.socket.send("set-passphrase"); + self.bridge.socket.send(passphrase); + return true; + } + /** * @returns {Promise} */ @@ -246,8 +260,7 @@ var vaultui = (function () { } var pinCode = await self.getUserEnterPin(); - var passphrase = await self.getUserPassphrase(); - self.bridge.socket.send(JSON.stringify({ pinCode: pinCode, passphrase: passphrase })); + self.bridge.socket.send(pinCode); var json = await self.bridge.waitBackendMessage(); if (json.hasOwnProperty("error")) { showError(json);