remove ability to use Passphrase if passphrase already in use; remove ability to choose to which seed apply passphrase - always apply to tmp seed if active and possible

This commit is contained in:
scgbckbone 2024-04-10 21:03:35 +02:00 committed by doc-hex
parent 2411c7566d
commit 3eee677648
8 changed files with 47 additions and 120 deletions

View File

@ -1,5 +1,9 @@
## 5.2.3 - 2024-XX-XX
- Change: `Passphrase` menu item is no longer offered if BIP39 passphrase
already in use. Use `Restore Master` with ability to keep or purge current
passphrase wallet settings.
- Change: Removed ability to add passphrase to master seed if active temporary seed.
- Bugfix: Saving passphrase on SD Card caused a freeze that required reboot
- Bugfix: Properly handle and finalize framing error response
- Bugfix: `Brick Me` option for `If Wrong` PIN caused yikes

View File

@ -25,6 +25,10 @@
## 1.1.1Q - 2024-04-XX
- Change: `Passphrase` menu item is no longer offered if BIP39 passphrase
already in use. Use `Restore Master` with ability to keep or purge current
passphrase wallet settings.
- Change: Removed ability to add passphrase to master seed if active temporary seed.
- Bugfix: Handle ZeroSecretException for BIP39 passphrase calculation when on temporary
seed without master secret.
- Bugfix: Battery idle timeout also considers last progress bar update

View File

@ -1207,35 +1207,27 @@ class NewPassphrase(UserAuthorizedAction):
from pincodes import pa
title = "Passphrase"
bypass_tmp = True
escape = "x2" + KEY_CANCEL
escape = "yx2" + KEY_CANCEL + KEY_ENTER
while 1:
msg = ('BIP-39 passphrase (%d chars long) has been provided over '
'USB connection. Should we switch to that wallet now?\n\n')
if pa.tmp_value and settings.get("words", True):
escape += "1"
msg += "Press (1) to add passphrase to currently active temporary seed. "
'USB connection. Should we switch to that wallet now?\n\n'
'Press OK to add passphrase ' % len(self._pw))
if pa.tmp_value:
msg += "to current active temporary seed. "
else:
msg += "to master seed. "
if not pa.is_secret_blank() and settings.master_get("words", True):
escape += "y" + KEY_ENTER
msg += "Press OK to add passphrase to master seed. "
msg += 'Press (2) to view the provided passphrase. X to cancel.'
msg += ('Press (2) to view the provided passphrase.\n\n'
'X to cancel.')
ch = await ux_show_story(msg=msg % len(self._pw), title=title,
escape=escape, strict_escape=True)
ch = await ux_show_story(msg=msg, title=title, escape=escape,
strict_escape=True)
if ch == '2':
await ux_show_story('Provided:\n\n%s\n\n' % self._pw, title=title)
continue
else:
if ch == '1':
bypass_tmp = False
break
else: break
try:
if ch not in 'y1'+ KEY_ENTER:
if ch not in ('y'+ KEY_ENTER):
# they don't want to!
self.refused = True
await ux_dramatic_pause("Refused.", 1)
@ -1243,9 +1235,7 @@ class NewPassphrase(UserAuthorizedAction):
from seed import set_bip39_passphrase
# full screen message shown: "Working..."
await set_bip39_passphrase(self._pw, bypass_tmp=bypass_tmp,
summarize_ux=False)
await set_bip39_passphrase(self._pw, summarize_ux=False)
self.result = settings.get('xpub')
except BaseException as exc:

View File

@ -85,10 +85,8 @@ def se2_and_real_secret():
from pincodes import pa
return (not pa.is_secret_blank()) and (not pa.tmp_value)
def bip39_passphrase_active():
import stash
return settings.get('words', True) \
or (settings.master_get('words', True) and stash.bip39_passphrase)
def word_based_seed():
return settings.get("words", True)
HWTogglesMenu = [
@ -243,7 +241,7 @@ DebugFunctionsMenu = [
SeedXORMenu = [
# xxxxxxxxxxxxxxxx
MenuItem("Split Existing", f=xor_split_start, predicate=lambda: settings.get('words', True)),
MenuItem("Split Existing", f=xor_split_start, predicate=word_based_seed),
MenuItem("Restore Seed XOR", f=xor_restore_start),
]
@ -252,8 +250,7 @@ SeedFunctionsMenu = [
MenuItem('Seed XOR', menu=SeedXORMenu),
MenuItem("Destroy Seed", f=clear_seed, predicate=se2_and_real_secret),
MenuItem('Lock Down Seed', f=convert_ephemeral_to_master),
MenuItem('Export SeedQR', f=export_seedqr,
predicate=lambda: settings.get('words', True)),
MenuItem('Export SeedQR', f=export_seedqr, predicate=word_based_seed),
]
DangerZoneMenu = [
@ -382,7 +379,7 @@ EmptyWallet = [
NormalSystem = [
# xxxxxxxxxxxxxxxx
MenuItem('Ready To Sign', f=ready2sign, shortcut='r'),
MenuItem('Passphrase', f=start_b39_pw, predicate=bip39_passphrase_active, shortcut='p'),
MenuItem('Passphrase', f=start_b39_pw, predicate=word_based_seed, shortcut='p'),
MenuItem('Scan Any QR Code', predicate=lambda: version.has_qr,
shortcut=KEY_QR, f=scan_any_qr, arg=(False, True)),
MenuItem('Start HSM Mode', f=start_hsm_menu_item, predicate=hsm_policy_available),

View File

@ -662,12 +662,7 @@ async def calc_bip39_passphrase(pw, bypass_tmp=False):
dis.fullscreen("Working...")
# get xfp of parent reliably - cannot go to settings for this if in ephemeral
current_xfp = settings.get("xfp", 0) if not pa.tmp_value else 0
if not current_xfp:
with stash.SensitiveValues(bypass_tmp=bypass_tmp) as sv:
assert sv.mode == 'words', sv.mode
current_xfp = swab32(sv.node.my_fp())
current_xfp = settings.get("xfp", 0)
with stash.SensitiveValues(bip39pw=pw, bypass_tmp=bypass_tmp) as sv:
# can't do it without original seed words (late, but caller has checked)
@ -1266,53 +1261,14 @@ class PassphraseMenu(MenuSystem):
cls.pp_sofar = ''
async def apply_pass_value(new_pp):
# Apply provided BIP-39 passphrase to master seed, and go to top menu.
mdata = None
tdata = None
# Apply provided BIP-39 passphrase to master or current active tmp seed
# and go to top menu.
nv, xfp, parent_xfp = await calc_bip39_passphrase(new_pp)
xfp_str = xfp2str(xfp)
parent_xfp_str = xfp2str(parent_xfp)
try:
m_nv, m_xfp, m_parent_xfp = await calc_bip39_passphrase(new_pp,
bypass_tmp=True)
m_parent_xfp_str = xfp2str(m_parent_xfp)
m_xfp_str = xfp2str(m_xfp)
mdata = (
m_nv, m_xfp, m_xfp_str, m_parent_xfp_str,
"master seed [%s]" % m_parent_xfp_str,
"(1) master+pass:\n%s%s\n\n" % (m_parent_xfp_str, m_xfp_str),
)
except (AssertionError, ZeroSecretException): pass
if pa.tmp_value and settings.get("words", True):
# we have ephemeral seed - can add passphrase to it as it is word based
t_nv, t_xfp, t_parent_xfp = await calc_bip39_passphrase(new_pp,
bypass_tmp=False)
t_parent_xfp_str = xfp2str(t_parent_xfp)
t_xfp_str = xfp2str(t_xfp)
tdata = (
t_nv, t_xfp, t_xfp_str, t_parent_xfp_str,
"current active temporary seed [%s]" % t_parent_xfp_str,
"(2) tmp+pass:\n%s%s\n\n" % (t_parent_xfp_str, t_xfp_str),
)
if tdata is None and mdata is None:
# if master is not word based, temporary has to be, otherwise "Passphrase"
# not offered in menu
# should never be seen by user because flow.py::bip39_passphrase_active
await ux_show_story(title="FAILED", msg="Need word based secret")
return
tmp = False
if tdata and mdata:
ch = await ux_show_story(mdata[-1] + tdata[-1], escape='12x',
strict_escape=True, scrollbar=False)
if ch == "x": return # exit
if ch == "2":
tmp = True
elif tdata:
tmp = True
data = tdata if tmp else mdata
nv, xfp, xfp_str, parent_xfp_str, msg, _ = data
msg = "current active temporary seed [%s]" if pa.tmp_value else "master seed [%s]"
msg = msg % parent_xfp_str
msg = ('Above is the master key fingerprint of the new wallet'
' created by adding passphrase to %s.'

View File

@ -552,11 +552,10 @@ class USBHandler:
if cmd == 'pass':
# bip39 passphrase provided, maybe use it if authorized
assert self.encrypted_req, 'must encrypt'
from flow import bip39_passphrase_active
from auth import start_bip39_passphrase
from glob import settings
assert bip39_passphrase_active(), 'no seed'
assert settings.get("words", True), 'no seed'
assert len(args) < 400, 'too long'
pw = str(args, 'utf8')
assert len(pw) < 100, 'too long'

View File

@ -93,10 +93,11 @@ def set_bip39_pw(dev, need_keypress, reset_seed_words, cap_story,
time.sleep(.1)
title, body = cap_story()
if on_tmp:
assert "Press (1)" in body
need_keypress("1")
assert "to current active temporary seed" in body
else:
press_select()
assert "to master seed" in body
press_select()
time.sleep(.3)
title, story = cap_story()
@ -232,12 +233,11 @@ def test_lockdown(stype, pick_menu_item, set_bip39_pw, goto_home, cap_story,
@pytest.mark.parametrize("stype", ["words", "xprv"])
@pytest.mark.parametrize("on_eph", [True, False])
@pytest.mark.parametrize("seed_vault", [True, False])
def test_bip39pass_on_ephemeral_seed(generate_ephemeral_words, import_ephemeral_xprv,
need_keypress, pick_menu_item, goto_home,
reset_seed_words, goto_eph_seed_menu, stype,
enter_complex, cap_story, cap_menu, on_eph,
enter_complex, cap_story, cap_menu,
settings_set, seed_vault, press_select):
passphrase = "@coinkite rulez!!"
reset_seed_words()
@ -246,8 +246,6 @@ def test_bip39pass_on_ephemeral_seed(generate_ephemeral_words, import_ephemeral_
goto_eph_seed_menu()
sim_fp = xfp2str(simulator_fixed_xfp)
if stype == "words":
# words
sec = generate_ephemeral_words(24, from_main=True, seed_vault=seed_vault)
@ -268,38 +266,18 @@ def test_bip39pass_on_ephemeral_seed(generate_ephemeral_words, import_ephemeral_
pick_menu_item("Passphrase")
press_select()
enter_complex(passphrase, apply=True)
time.sleep(.1)
title, choice_story = cap_story()
tmp_seed = Mnemonic.to_seed(" ".join(sec), passphrase=passphrase)
tmp_node = BIP32Node.from_master_secret(tmp_seed)
tmp_fp = tmp_node.fingerprint().hex().upper()
master_seed = Mnemonic.to_seed(simulator_fixed_words, passphrase=passphrase)
master_node = BIP32Node.from_master_secret(master_seed)
master_fp = master_node.fingerprint().hex().upper()
choice_msg = "(1) master+pass:\n%s%s\n\n" % (sim_fp, master_fp)
choice_msg += "(2) tmp+pass:\n%s%s\n\n" % (parent_fp, tmp_fp)
assert choice_story == choice_msg
if on_eph:
need_keypress("2")
else:
need_keypress("1")
time.sleep(.2)
title, story = cap_story()
title_xfp = title[1:-1]
assert "created by adding passphrase to" in story
if on_eph:
assert tmp_fp == title_xfp
assert f"current active temporary seed [{parent_fp}]" in story
else:
assert master_fp == title_xfp
assert f"master seed [{sim_fp}]" in story
assert tmp_fp == title_xfp
assert f"current active temporary seed [{parent_fp}]" in story
press_select()
@ -333,10 +311,7 @@ def test_bip39pass_on_ephemeral_seed(generate_ephemeral_words, import_ephemeral_
time.sleep(.1)
_, story = cap_story()
assert title_xfp in story
if on_eph:
assert ("BIP-39 Passphrase on [%s]" % parent_fp) in story
else:
assert "BIP-39 Passphrase on [0F056943]" in story
assert ("BIP-39 Passphrase on [%s]" % parent_fp) in story
@pytest.mark.parametrize("stype", ["words", "xprv", "b39pw"])
@ -345,9 +320,9 @@ def test_bip39pass_on_ephemeral_seed_usb(generate_ephemeral_words, import_epheme
reset_seed_words, goto_eph_seed_menu, stype,
cap_story, cap_menu, set_bip39_pw,
get_identity_story, settings_set):
settings_set("seedvault", 0)
passphrase = "@coinkite rulez!!"
reset_seed_words()
settings_set("seedvault", 0)
goto_eph_seed_menu()
@ -364,7 +339,7 @@ def test_bip39pass_on_ephemeral_seed_usb(generate_ephemeral_words, import_epheme
import_ephemeral_xprv("sd", from_main=True, seed_vault=False)
goto_home()
if stype == "xprv":
if stype in ("xprv", "b39pw"):
with pytest.raises(Exception) as e:
set_bip39_pw(passphrase, reset=False)
assert "no seed" in e.value.args[0]

View File

@ -627,6 +627,8 @@ def test_bip39_complex(target, goto_home, pick_menu_item, cap_story,
enter_complex(target, apply=True)
press_select()
verify_ephemeral_secret_ui(xpub=expect.hwif(), is_b39pw=True)
pick_menu_item("Restore Master")
press_select()
@pytest.mark.qrcode