confirm the passphrase in the wallet-level and terminal seed imports
What changed, and why it matters
This commit adds a 'confirm your passphrase' step when importing a Bitcoin wallet seed in two places: the desktop wallet import screen and the terminal/command-line wallet creation. Previously, if a user typed a BIP39 passphrase (the extra password that unlocks a hidden wallet) with a typo, Sparrow would silently create a different wallet. The user might then send funds to addresses they do not actually control or cannot later reproduce. The fix forces the user to type the passphrase twice so typos are caught immediately.
Users importing BIP39 seeds with a passphrase should upgrade to the version containing this commit to avoid accidental wallet derivation from typos. Developers should review whether other passphrase entry points (e.g., hardware wallet unlock, wallet loading, signing flows) also need confirmation.
Security signals we found
Prevents silent derivation of an unintended wallet from a mistyped BIP39 passphrase
Adds user-input confirmation to reduce risk of fund loss / inaccessible wallet
No cryptographic weakness fixed; the issue is usability leading to loss of funds
Patch is partial: only covers wallet-level and terminal seed imports, not all passphrase entry points
Evidence from the diff
The patch introduces passphrase confirmation dialogs in MnemonicWalletKeystoreImportPane (GUI) and Bip39Dialog (terminal). It reuses KeystorePassphraseDialog and a new confirm=true constructor on PassphraseDialog. If the re-entered passphrase does not match the original entry, import/create is aborted with an ImportException or error dialog. This is a defensive UX fix, not a cryptographic change; it prevents silent derivation of the wrong seed/wallet due to user input error.
Changed components
src/main/java/com/sparrowwallet/sparrow/control/MnemonicWalletKeystoreImportPane.javasrc/main/java/com/sparrowwallet/sparrow/terminal/PassphraseDialog.javasrc/main/java/com/sparrowwallet/sparrow/terminal/wallet/Bip39Dialog.javaInspect captured patch +40 / −2
### src/main/java/com/sparrowwallet/sparrow/control/MnemonicWalletKeystoreImportPane.java
@@ -218,6 +218,15 @@ public PolicyAndScriptType fromString(String string) {
try {
PolicyAndScriptType type = comboBox.getValue();
Wallet wallet = getWallet(type.policyType(), type.scriptType(), type.scriptType().getDefaultDerivation());
+ if(passphraseProperty.get() != null && !passphraseProperty.get().isEmpty()) {
+ KeystorePassphraseDialog keystorePassphraseDialog = new KeystorePassphraseDialog(null, wallet.getKeystores().getFirst(), true);
+ keystorePassphraseDialog.initOwner(this.getScene().getWindow());
+ Optional<String> optPassphrase = keystorePassphraseDialog.showAndWait();
+ if(optPassphrase.isEmpty() || !optPassphrase.get().equals(passphraseProperty.get())) {
+ throw new ImportException("Re-entered passphrase did not match");
+ }
+ }
+
EventManager.get().post(new WalletImportEvent(wallet));
} catch(ImportException e) {
log.error("Error importing mnemonic", e);
### src/main/java/com/sparrowwallet/sparrow/terminal/PassphraseDialog.java
@@ -15,7 +15,11 @@ public class PassphraseDialog extends DialogWindow {
private String result;
public PassphraseDialog(String walletName, Keystore keystore) {
- super("Passphrase for " + walletName);
+ this(walletName, keystore, false);
+ }
+
+ public PassphraseDialog(String walletName, Keystore keystore, boolean confirm) {
+ super((confirm ? "Confirm Passphrase for " : "Passphrase for ") + walletName);
setHints(Collections.singleton(Window.Hint.CENTERED));
@@ -30,7 +34,7 @@ public PassphraseDialog(String walletName, Keystore keystore) {
Panel mainPanel = new Panel();
mainPanel.setLayoutManager(new GridLayout(1).setLeftMarginSize(1).setRightMarginSize(1));
- mainPanel.addComponent(new Label("Enter the BIP39 passphrase for keystore:\n" + keystore.getLabel()));
+ mainPanel.addComponent(new Label(confirm ? "Re-enter the BIP39 passphrase\nto confirm:" : "Enter the BIP39 passphrase for keystore:\n" + keystore.getLabel()));
mainPanel.addComponent(new EmptySpace(TerminalSize.ONE));
passphrase.setLayoutData(GridLayout.createLayoutData(GridLayout.Alignment.FILL, GridLayout.Alignment.CENTER, true, false)).addTo(mainPanel);
mainPanel.addComponent(new EmptySpace(TerminalSize.ONE));
### src/main/java/com/sparrowwallet/sparrow/terminal/wallet/Bip39Dialog.java
@@ -12,6 +12,7 @@
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.io.Bip39;
import com.sparrowwallet.sparrow.io.ImportException;
+import com.sparrowwallet.sparrow.terminal.PassphraseDialog;
import com.sparrowwallet.sparrow.terminal.SparrowTerminal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -23,6 +24,8 @@
import java.util.Date;
import java.util.List;
+import static com.sparrowwallet.sparrow.AppServices.showErrorDialog;
+
public class Bip39Dialog extends NewWalletDialog {
private static final Logger log = LoggerFactory.getLogger(Bip39Dialog.class);
public static final int MAX_COLUMNS = 40;
@@ -100,6 +103,28 @@ public Bip39Dialog(String walletName) {
});
}
+ @Override
+ protected void createWallet() {
+ if(!passphrase.getText().isEmpty()) {
+ try {
+ PolicyAndScriptType type = scriptType.getSelectedItem();
+ Keystore keystore = importer.getKeystore(type.policyType(), type.scriptType().getDefaultDerivation(), getWords(), passphrase.getText());
+ PassphraseDialog passphraseDialog = new PassphraseDialog(walletName, keystore, true);
+ String confirmation = passphraseDialog.showDialog(SparrowTerminal.get().getGui());
+ if(confirmation == null || !confirmation.equals(passphrase.getText())) {
+ showErrorDialog("Error Creating Wallet", "Re-entered passphrase did not match.");
+ return;
+ }
+ } catch(ImportException e) {
+ log.error("Cannot create keystore", e);
+ showErrorDialog("Error Creating Wallet", e.getMessage());
+ return;
+ }
+ }
+
+ super.createWallet();
+ }
+
private void generateNew() {
WordNumberDialog wordNumberDialog = new WordNumberDialog();
Integer numberOfWords = wordNumberDialog.showDialog(SparrowTerminal.get().getGui());Why this scored 34/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.