gui: prune bip32 derivations when signing with BitBox02

It can otherwise lead to the BitBox02 not signing for the correct public
key. See
https://github.com/wizardsardine/liana/pull/706#issuecomment-1744705808
for details.
This commit is contained in:
Antoine Poinsot 2023-10-27 18:10:32 +02:00
parent b8d6be82a7
commit 99bbb76228
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
3 changed files with 34 additions and 3 deletions

View File

@ -1,7 +1,7 @@
use std::convert::From; use std::convert::From;
use std::io::ErrorKind; use std::io::ErrorKind;
use liana::config::ConfigError; use liana::{config::ConfigError, descriptors::LianaDescError};
use crate::{ use crate::{
app::{settings::SettingsError, wallet::WalletError}, app::{settings::SettingsError, wallet::WalletError},
@ -15,6 +15,7 @@ pub enum Error {
Daemon(DaemonError), Daemon(DaemonError),
Unexpected(String), Unexpected(String),
HardwareWallet(async_hwi::Error), HardwareWallet(async_hwi::Error),
Desc(LianaDescError),
} }
impl std::fmt::Display for Error { impl std::fmt::Display for Error {
@ -48,6 +49,7 @@ impl std::fmt::Display for Error {
}, },
Self::Unexpected(e) => write!(f, "Unexpected error: {}", e), Self::Unexpected(e) => write!(f, "Unexpected error: {}", e),
Self::HardwareWallet(e) => write!(f, "{}", e), Self::HardwareWallet(e) => write!(f, "{}", e),
Self::Desc(e) => write!(f, "Liana descriptor error: {}", e),
} }
} }
} }

View File

@ -365,7 +365,7 @@ impl Action for SignAction {
self.processing = true; self.processing = true;
let psbt = tx.psbt.clone(); let psbt = tx.psbt.clone();
return Command::perform( return Command::perform(
sign_psbt(device.clone(), *fingerprint, psbt), sign_psbt(self.wallet.clone(), device.clone(), *fingerprint, psbt),
Message::Signed, Message::Signed,
); );
} }
@ -449,11 +449,39 @@ async fn sign_psbt_with_hot_signer(
} }
async fn sign_psbt( async fn sign_psbt(
wallet: Arc<Wallet>,
hw: std::sync::Arc<dyn async_hwi::HWI + Send + Sync>, hw: std::sync::Arc<dyn async_hwi::HWI + Send + Sync>,
fingerprint: Fingerprint, fingerprint: Fingerprint,
mut psbt: Psbt, mut psbt: Psbt,
) -> Result<(Psbt, Fingerprint), Error> { ) -> Result<(Psbt, Fingerprint), Error> {
hw.sign_tx(&mut psbt).await.map_err(Error::from)?; // The BitBox02 is only going to produce a signature for a single key in the Script. In order
// to make sure it doesn't sign for a public key from another spending path we remove the BIP32
// derivation for the other paths.
if matches!(hw.device_kind(), async_hwi::DeviceKind::BitBox02) {
// We need to make sure we don't prune the BIP32 derivations from the original PSBT (which
// would end up being updated in the daemon's database and erase the previously unpruned
// one). To this end we create a new, pruned, psbt we use for signing and then merge its
// signatures back into the original PSBT.
let mut pruned_psbt = wallet
.main_descriptor
.prune_bip32_derivs_last_avail(psbt.clone())
.map_err(Error::Desc)?;
hw.sign_tx(&mut pruned_psbt).await.map_err(Error::from)?;
for (i, psbt_in) in psbt.inputs.iter_mut().enumerate() {
if let Some(pruned_psbt_in) = pruned_psbt.inputs.get_mut(i) {
psbt_in
.partial_sigs
.append(&mut pruned_psbt_in.partial_sigs);
} else {
log::error!(
"Not all PSBT inputs are present in the pruned psbt. Pruned psbt: '{}'.",
&pruned_psbt
);
}
}
} else {
hw.sign_tx(&mut psbt).await.map_err(Error::from)?;
}
Ok((psbt, fingerprint)) Ok((psbt, fingerprint))
} }

View File

@ -37,6 +37,7 @@ impl From<&Error> for WarningMessage {
}, },
Error::Unexpected(_) => WarningMessage("Unknown error".to_string()), Error::Unexpected(_) => WarningMessage("Unknown error".to_string()),
Error::HardwareWallet(_) => WarningMessage("Hardware wallet error".to_string()), Error::HardwareWallet(_) => WarningMessage("Hardware wallet error".to_string()),
Error::Desc(e) => WarningMessage(format!("Descriptor analysis error: '{}'.", e)),
} }
} }
} }