You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
key restore ignores --password, so restoring a passphrase-protected mnemonic gives the wrong key #372
Describe the bug key restore --password <PASSPHRASE> silently ignores the passphrase. The restored xprv and fingerprint are always derived from the mnemonic alone, so a key generated with key generate --password cannot be restored with the same mnemonic and password.
The cause is in src/handlers/key.rs, RestoreKeyCommand::execute:
let xkey:ExtendedKey = (mnemonic.clone(),&self.password).0.into_extended_key()?;
.0 takes only the first element of the tuple (the Mnemonic), so self.password is discarded before into_extended_key() runs. GenerateKeyCommand uses the password correctly:
let xkey:ExtendedKey = (mnemonic.clone(),self.password.clone()).into_extended_key()?;
To Reproduce
bdk-cli key generate --password foo
# note the "mnemonic" and "xprv"
bdk-cli key restore --mnemonic "<mnemonic from above>" --password foo
Expected behavior
The restored xprv and fingerprint match the generated ones.
Actual: they differ. The result is the same as running key restore with no --password. Anyone who backs up a mnemonic plus a BIP-39 passphrase and later restores it with bdk-cli gets a different wallet, which looks empty, with no error or warning.
The issue is real but implementation is wrong. you must have been thinking about MnemonicWithPassphrase from bdk_wallet::keys::bip39 but that type has no method at all as of the version of bdk_wallet used.
let xkey:ExtendedKey = (mnemonic.clone(),self.password.clone()).into_extended_key()?;
This wouldn't even pass cargo check. Since you are already working on it the correct implementation of this is :
let xkey:ExtendedKey = mnemonic.clone().into_extended_key()?;let xprv = xkey.into_xprv(ctx.network.into()).ok_or_else(|| {Error::Generic("Privatekey info not found (should not happen)".to_string())})?;let xprv = ifletSome(passphrase) = &self.password{let seed = mnemonic.to_seed(passphrase);Xpriv::new_master(ctx.network,&seed)?
}else{
xprv
};
Thanks for taking a look, @emma31-dev. This does compile: cargo check passes on this branch and the new test runs in CI. MnemonicWithPassphrase is a type alias for (Mnemonic, Option<String>), and into_extended_key comes from the DerivableKey impl for it in bdk_wallet 3.1.0 (src/keys/bip39.rs). That impl calls mnemonic.to_seed(passphrase) and builds the master key, the same steps as your snippet. It's also the line GenerateKeyCommand already uses, so keeping it keeps generate and restore consistent. @emma31-dev@tvpeter@vadim-anfv please can you take a look.
Activity
fix(key): use password when restoring a key
emma31-dev commentedon Nov 5, 2026
The issue is real but implementation is wrong. you must have been thinking about
MnemonicWithPassphrasefrombdk_wallet::keys::bip39but that type has no method at all as of the version ofbdk_walletused.This wouldn't even pass cargo check. Since you are already working on it the correct implementation of this is :
Thanks for addressing this.
chukwudiikeh commentedon Nov 6, 2026
Thanks for taking a look, @emma31-dev. This does compile:
cargo checkpasses on this branch and the new test runs in CI.MnemonicWithPassphraseis a type alias for(Mnemonic, Option<String>), andinto_extended_keycomes from theDerivableKeyimpl for it in bdk_wallet 3.1.0 (src/keys/bip39.rs). That impl callsmnemonic.to_seed(passphrase)and builds the master key, the same steps as your snippet. It's also the lineGenerateKeyCommandalready uses, so keeping it keeps generate and restore consistent. @emma31-dev @tvpeter @vadim-anfv please can you take a look.fix(key): use password when restoring a key
vadim-anfv commentedon Nov 9, 2026
@chukwudiikeh great find! The suggested fix looks right to me. I checked the behaviour locally: the bug reproduces on master and is gone on #373.
I will finish reviewing #373 shortly.
Merge bitcoindevkit#373: fix(key): use password when restoring a key
refactor(key): share derivation between generate and restore
refactor(key): share derivation between generate and restore
refactor(key): share derivation between generate and restore