From b95fbbb86f2b7e6575e64ffdf7fdd9766387d403 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Mon, 10 Jun 2024 19:35:56 +0000 Subject: [PATCH] wizard: fix regression: allow passphrase for some '2fa' seeds fixes https://github.com/spesmilo/electrum/issues/9088 --- electrum/mnemonic.py | 17 +++++++++++++++++ electrum/wizard.py | 5 ++--- tests/test_mnemonic.py | 21 ++++++++++++++++++++- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/electrum/mnemonic.py b/electrum/mnemonic.py index d458c68c7..04661ec3c 100644 --- a/electrum/mnemonic.py +++ b/electrum/mnemonic.py @@ -276,6 +276,23 @@ def seed_type(x: str) -> str: return '' +def can_seed_have_passphrase(seed: str) -> bool: + stype = seed_type(seed) + if not stype: + raise Exception(f'unexpected seed type: {stype!r}') + if stype == 'old': + return False + if stype == '2fa': + # post-version-2.7 2fa seeds can have passphrase, but older ones cannot + num_words = len(seed.split()) + if num_words == 12: + return True + else: + return False + # all other types can have a seed extension/passphrase + return True + + def is_seed(x: str) -> bool: return bool(seed_type(x)) diff --git a/electrum/wizard.py b/electrum/wizard.py index cd76941a4..365c08c26 100644 --- a/electrum/wizard.py +++ b/electrum/wizard.py @@ -13,7 +13,7 @@ from electrum.storage import WalletStorage, StorageEncryptionVersion from electrum.wallet_db import WalletDB from electrum.bip32 import normalize_bip32_derivation, xpub_type from electrum import keystore, mnemonic, bitcoin -from electrum.mnemonic import is_any_2fa_seed_type +from electrum.mnemonic import is_any_2fa_seed_type, can_seed_have_passphrase if TYPE_CHECKING: from electrum.daemon import Daemon @@ -492,8 +492,7 @@ class NewWalletWizard(AbstractWizard): seed_type = mnemonic.seed_type(seed) if seed_type != '': seed_valid = True - if seed_type in ['old', '2fa']: - can_passphrase = False + can_passphrase = can_seed_have_passphrase(seed) elif seed_variant == 'bip39': is_checksum, is_wordlist = keystore.bip39_is_checksum_valid(seed) validation_message = ('' if is_checksum else _('BIP39 checksum failed')) if is_wordlist else _('Unknown BIP39 wordlist') diff --git a/tests/test_mnemonic.py b/tests/test_mnemonic.py index 45a10df26..b89ac2673 100644 --- a/tests/test_mnemonic.py +++ b/tests/test_mnemonic.py @@ -7,7 +7,7 @@ from electrum import mnemonic from electrum import slip39 from electrum import old_mnemonic from electrum.util import bfh -from electrum.mnemonic import is_new_seed, is_old_seed, seed_type, is_matching_seed +from electrum.mnemonic import is_new_seed, is_old_seed, seed_type, is_matching_seed, can_seed_have_passphrase from electrum.version import SEED_PREFIX_SW, SEED_PREFIX from . import ElectrumTestCase @@ -244,6 +244,25 @@ class Test_seeds(ElectrumTestCase): self.assertFalse(is_matching_seed(seed="when blade focus", seed_again="when bl4de focus")) self.assertFalse(is_matching_seed(seed="when blade focus", seed_again="when bla4de focus")) + def test_can_seed_have_passphrase(self): + seed_invalid = 'xxx' + with self.assertRaises(Exception): + self.assertFalse(can_seed_have_passphrase(seed_invalid)) + seed_old = 'cell dumb heartbeat north boom tease ship baby bright kingdom rare squeeze' + self.assertFalse(can_seed_have_passphrase(seed_old)) + seed_standard = 'cram swing cover prefer miss modify ritual silly deliver chunk behind inform able' + self.assertTrue(can_seed_have_passphrase(seed_standard)) + seed_segwit = 'frost pig brisk excite novel report camera enlist axis nation novel desert' + self.assertTrue(can_seed_have_passphrase(seed_segwit)) + seed_2fa_12 = 'science dawn member doll dutch real can brick knife deny drive list' + self.assertTrue(can_seed_have_passphrase(seed_2fa_12)) + seed_2fa_24 = 'sibling leg cable timber patient foot occur plate travel finger chef scale radio citizen promote immune must chef fluid sea sphere common acid lab' + self.assertFalse(can_seed_have_passphrase(seed_2fa_24)) + seed_2fa_25 = 'bind clever room kidney crucial sausage spy edit canvas soul liquid ribbon slam open alpha suffer gate relax voice carpet law hill woman tonight abstract' + self.assertFalse(can_seed_have_passphrase(seed_2fa_25)) + seed_2fa_segwit = 'agree install' + self.assertTrue(can_seed_have_passphrase(seed_2fa_segwit)) + class Test_slip39(ElectrumTestCase): """ Test SLIP39 test vectors. """