Allow user to input a passphrase for Trezor v1 (Fix #5794) (#5980)

This commit is contained in:
Nicolas Dorier
2024-05-15 09:18:20 +09:00
committed by GitHub
parent 69e0ae76c7
commit 7793c5e5df
4 changed files with 42 additions and 25 deletions

View File

@@ -60,7 +60,11 @@ namespace BTCPayServer.Controllers
HDFingerprint? fingerprint = null; HDFingerprint? fingerprint = null;
string password = null; string password = null;
var websocketHelper = new WebSocketHelper(websocket); var websocketHelper = new WebSocketHelper(websocket);
async Task FetchFingerprint()
{
fingerprint = (await device.GetXPubAsync(new KeyPath("44'"), cancellationToken)).ExtPubKey.ParentFingerprint;
device = new HwiDeviceClient(hwi, DeviceSelectors.FromFingerprint(fingerprint.Value), deviceEntry.Model, fingerprint) { Password = password };
}
async Task<bool> RequireDeviceUnlocking() async Task<bool> RequireDeviceUnlocking()
{ {
if (deviceEntry == null) if (deviceEntry == null)
@@ -91,6 +95,13 @@ namespace BTCPayServer.Controllers
return true; return true;
} }
} }
if (IsTrezorOne(deviceEntry) && password is null)
{
fingerprint = null; // There will be a new fingerprint
device = new HwiDeviceClient(hwi, DeviceSelectors.FromDeviceType("trezor", deviceEntry.Path), deviceEntry.Model, null);
await websocketHelper.Send("{ \"error\": \"need-passphrase\"}", cancellationToken);
return true;
}
return false; return false;
} }
@@ -118,7 +129,7 @@ namespace BTCPayServer.Controllers
} }
if (fingerprint is null) if (fingerprint is null)
{ {
fingerprint = (await device.GetXPubAsync(new KeyPath("44'"), cancellationToken)).ExtPubKey.ParentFingerprint; await FetchFingerprint();
} }
await websocketHelper.Send("{ \"info\": \"ready\"}", cancellationToken); await websocketHelper.Send("{ \"info\": \"ready\"}", cancellationToken);
o = JObject.Parse(await websocketHelper.NextMessageAsync(cancellationToken)); o = JObject.Parse(await websocketHelper.NextMessageAsync(cancellationToken));
@@ -211,14 +222,26 @@ namespace BTCPayServer.Controllers
var factory = network.NBXplorerNetwork.DerivationStrategyFactory; var factory = network.NBXplorerNetwork.DerivationStrategyFactory;
if (fingerprint is null) if (fingerprint is null)
{ {
fingerprint = (await device.GetXPubAsync(new KeyPath("44'"), cancellationToken)).ExtPubKey.ParentFingerprint; await FetchFingerprint();
} }
result["fingerprint"] = fingerprint.Value.ToString(); result["fingerprint"] = fingerprint.Value.ToString();
DerivationStrategyBase strategy = null; DerivationStrategyBase strategy = null;
KeyPath keyPath = null;
BitcoinExtPubKey xpub = null;
KeyPath keyPath = (addressType switch
{
"taproot" => new KeyPath("86'"),
"segwit" => new KeyPath("84'"),
"segwitWrapped" => new KeyPath("49'"),
"legacy" => new KeyPath("44'"),
_ => null
})?.Derive(network.CoinType).Derive(accountNumber, true);
if (keyPath is null)
{
await websocketHelper.Send("{ \"error\": \"invalid-addresstype\"}", cancellationToken);
continue;
}
BitcoinExtPubKey xpub = await device.GetXPubAsync(keyPath);
if (!network.NBitcoinNetwork.Consensus.SupportSegwit && addressType != "legacy") if (!network.NBitcoinNetwork.Consensus.SupportSegwit && addressType != "legacy")
{ {
await websocketHelper.Send("{ \"error\": \"segwit-notsupported\"}", cancellationToken); await websocketHelper.Send("{ \"error\": \"segwit-notsupported\"}", cancellationToken);
@@ -232,8 +255,6 @@ namespace BTCPayServer.Controllers
} }
if (addressType == "taproot") if (addressType == "taproot")
{ {
keyPath = new KeyPath("86'").Derive(network.CoinType).Derive(accountNumber, true);
xpub = await device.GetXPubAsync(keyPath);
strategy = factory.CreateDirectDerivationStrategy(xpub, new DerivationStrategyOptions() strategy = factory.CreateDirectDerivationStrategy(xpub, new DerivationStrategyOptions()
{ {
ScriptPubKeyType = ScriptPubKeyType.TaprootBIP86 ScriptPubKeyType = ScriptPubKeyType.TaprootBIP86
@@ -241,8 +262,6 @@ namespace BTCPayServer.Controllers
} }
else if (addressType == "segwit") else if (addressType == "segwit")
{ {
keyPath = new KeyPath("84'").Derive(network.CoinType).Derive(accountNumber, true);
xpub = await device.GetXPubAsync(keyPath);
strategy = factory.CreateDirectDerivationStrategy(xpub, new DerivationStrategyOptions() strategy = factory.CreateDirectDerivationStrategy(xpub, new DerivationStrategyOptions()
{ {
ScriptPubKeyType = ScriptPubKeyType.Segwit ScriptPubKeyType = ScriptPubKeyType.Segwit
@@ -250,8 +269,6 @@ namespace BTCPayServer.Controllers
} }
else if (addressType == "segwitWrapped") else if (addressType == "segwitWrapped")
{ {
keyPath = new KeyPath("49'").Derive(network.CoinType).Derive(accountNumber, true);
xpub = await device.GetXPubAsync(keyPath);
strategy = factory.CreateDirectDerivationStrategy(xpub, new DerivationStrategyOptions() strategy = factory.CreateDirectDerivationStrategy(xpub, new DerivationStrategyOptions()
{ {
ScriptPubKeyType = ScriptPubKeyType.SegwitP2SH ScriptPubKeyType = ScriptPubKeyType.SegwitP2SH
@@ -259,18 +276,12 @@ namespace BTCPayServer.Controllers
} }
else if (addressType == "legacy") else if (addressType == "legacy")
{ {
keyPath = new KeyPath("44'").Derive(network.CoinType).Derive(accountNumber, true);
xpub = await device.GetXPubAsync(keyPath);
strategy = factory.CreateDirectDerivationStrategy(xpub, new DerivationStrategyOptions() strategy = factory.CreateDirectDerivationStrategy(xpub, new DerivationStrategyOptions()
{ {
ScriptPubKeyType = ScriptPubKeyType.Legacy ScriptPubKeyType = ScriptPubKeyType.Legacy
}); });
} }
else
{
await websocketHelper.Send("{ \"error\": \"invalid-addresstype\"}", cancellationToken);
continue;
}
result.Add(new JProperty("strategy", strategy.ToString())); result.Add(new JProperty("strategy", strategy.ToString()));
result.Add(new JProperty("accountKey", xpub.ToString())); result.Add(new JProperty("accountKey", xpub.ToString()));
result.Add(new JProperty("keyPath", keyPath.ToString())); result.Add(new JProperty("keyPath", keyPath.ToString()));
@@ -315,7 +326,6 @@ askdevice:
fingerprint = device.Fingerprint; fingerprint = device.Fingerprint;
JObject json = new JObject(); JObject json = new JObject();
json.Add("model", model); json.Add("model", model);
json.Add("fingerprint", device.Fingerprint?.ToString());
await websocketHelper.Send(json.ToString(), cancellationToken); await websocketHelper.Send(json.ToString(), cancellationToken);
break; break;
} }
@@ -386,6 +396,10 @@ askdevice:
{ {
return deviceEntry.Model.Contains("Trezor_T", StringComparison.OrdinalIgnoreCase); return deviceEntry.Model.Contains("Trezor_T", StringComparison.OrdinalIgnoreCase);
} }
private static bool IsTrezorOne(HwiEnumerateEntry deviceEntry)
{
return deviceEntry.Model.Contains("trezor_1", StringComparison.OrdinalIgnoreCase);
}
public StoreData CurrentStore public StoreData CurrentStore
{ {

View File

@@ -41,8 +41,10 @@
</div> </div>
<div id="passphrase-input" class="mt-4" style="display: none;"> <div id="passphrase-input" class="mt-4" style="display: none;">
<div class="form-group"> <div class="form-group">
<label for="Password" class="input-group-text"><span class="input-group-addon fa fa-lock"></span></label> <div class="input-group">
<input id="Password" type="password" class="form-control" placeholder="Passphrase" /> <label for="Password" class="input-group-text"><span class="input-group-addon fa fa-lock"></span></label>
<input id="Password" type="password" class="form-control" placeholder="Passphrase (Leave empty if there isn't any passphrase)" />
</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">

View File

@@ -47,6 +47,7 @@
</form> </form>
</div> </div>
</template> </template>
<div id="btcpayservervault" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="btcpayservervault" aria-hidden="true"></div> <div id="btcpayservervault" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="btcpayservervault" aria-hidden="true"></div>
<form method="post" asp-controller="UIStores" asp-action="UpdateWallet" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode"> <form method="post" asp-controller="UIStores" asp-action="UpdateWallet" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode">
@@ -111,7 +112,6 @@
document.querySelectorAll("[data-address]").forEach(link => { document.querySelectorAll("[data-address]").forEach(link => {
link.addEventListener("click", async event => { link.addEventListener("click", async event => {
event.preventDefault(); event.preventDefault();
const $link = event.currentTarget; const $link = event.currentTarget;
const address = JSON.parse($link.dataset.address); const address = JSON.parse($link.dataset.address);
const rootedKeyPath = JSON.parse($link.dataset.rootedKeyPath); const rootedKeyPath = JSON.parse($link.dataset.rootedKeyPath);
@@ -132,8 +132,9 @@
vaultUI.closeBridge(); vaultUI.closeBridge();
}); });
while (!await vaultUI.askForDevice()) {} $$modal.modal("show");
while (!await vaultUI.askForDevice()) {}
await vaultUI.askForDisplayAddress(rootedKeyPath); await vaultUI.askForDisplayAddress(rootedKeyPath);
$$modal.modal("hide"); $$modal.modal("hide");

View File

@@ -1,7 +1,7 @@
@model WalletSetupViewModel @model WalletSetupViewModel
@{ @{
Layout = "_LayoutWalletSetup"; Layout = "_LayoutWalletSetup";
ViewData.SetActivePage(StoreNavPages.OnchainSettings, "Connect your hardware wallet", Context.GetStoreData().Id); ViewData.SetActivePage(StoreNavPages.OnchainSettings, "Connect your hardware wallet", Context.GetStoreData().Id);
} }
@section Navbar { @section Navbar {