diff --git a/releases/ChangeLog.md b/releases/ChangeLog.md index 2fddb162..64877855 100644 --- a/releases/ChangeLog.md +++ b/releases/ChangeLog.md @@ -17,6 +17,7 @@ - Enhancement: One instant retry on SE1 comm failures - Enhancement: Allow passphrase via USB if passphrase already set - operates on master seed. - Enhancement: Continuation of removal of obsolete Mk2/Mk3 code-paths from master branch. +- Enhancement: Improve BIP39 Passphrase UX when temporary seed is active and applicable. - Bugfix: Handle any failures in slot reading when loading settings - Bugfix: Add missing First Time UX for extended key import as master seed - Bugfix: Hide `Upgrade Firmware` menu item if temporary seed is active diff --git a/shared/seed.py b/shared/seed.py index d07941ac..4a29342e 100644 --- a/shared/seed.py +++ b/shared/seed.py @@ -1194,30 +1194,41 @@ class PassphraseMenu(MenuSystem): # empty string here - noop return - nv, xfp, parent_xfp = await calc_bip39_passphrase(pp_sofar, bypass_tmp=True) - - msg = ('Above is the master key fingerprint of the new wallet. ' - 'Press X to abort and keep editing passphrase, ' - 'OK to use the new wallet, (1) to use and save to MicroSD') - - msg1 = "" + nv, xfp, parent_xfp = await calc_bip39_passphrase(pp_sofar, + bypass_tmp=True) + parent_xfp_str = xfp2str(parent_xfp) + xfp_str = xfp2str(xfp) + msg0 = "master seed [%s]" % parent_xfp_str if pa.tmp_value and settings.get("words", True): - # we have ephemeral seed but can add passphrase to it as it is word based - msg1 = (", or press (2) to add passphrase to the current " - "active temporary seed instead of the main seed.") + # 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(pp_sofar, + bypass_tmp=False) + t_parent_xfp_str = xfp2str(t_parent_xfp) + t_xfp_str = xfp2str(t_xfp) + choice_msg = "(1) master+pass:\n%s→%s\n\n" % (parent_xfp_str, xfp_str) + choice_msg += "(2) tmp+pass:\n%s→%s\n\n" % (t_parent_xfp_str, t_xfp_str) + ch = await ux_show_story(choice_msg, escape='12x', strict_escape=True, + scrollbar=False) + if ch == "x": return # exit + if ch == "2": + parent_xfp_str = t_parent_xfp_str + xfp = t_xfp + xfp_str = t_xfp_str + msg0 = "current active temporary seed [%s]" % t_parent_xfp_str + nv = t_nv - ch = await ux_show_story(msg + msg1, title="[%s]" % xfp2str(xfp), escape='12') + msg = ('Above is the master key fingerprint of the new wallet' + ' created by adding passphrase to %s.' + ' Press X to abort and keep editing passphrase,' + ' OK to use the new wallet, (1) to use' + ' and save to MicroSD') % msg0 + + ch = await ux_show_story(msg, title="[%s]" % xfp_str, escape='1') if ch == 'x': return - if ch == "2": - stash.SensitiveValues.clear_cache() - nv, xfp, parent_xfp = await calc_bip39_passphrase(pp_sofar, bypass_tmp=False) - ch = await ux_show_story(msg, title="[%s]" % xfp2str(xfp), escape='1') - if ch == "x": return - await set_ephemeral_seed(nv, summarize_ux=False, bip39pw=pp_sofar, - meta="BIP-39 Passphrase on [%s]" % xfp2str(parent_xfp)) + meta="BIP-39 Passphrase on [%s]" % parent_xfp_str) if ch == '1': await PassphraseSaver().append(xfp, pp_sofar) diff --git a/shared/ux.py b/shared/ux.py index 70518038..ae3226f0 100644 --- a/shared/ux.py +++ b/shared/ux.py @@ -173,7 +173,8 @@ class PressRelease: # (using FontSmall) CH_PER_W = const(17) -async def ux_show_story(msg, title=None, escape=None, sensitive=False, strict_escape=False): +async def ux_show_story(msg, title=None, escape=None, sensitive=False, + strict_escape=False, scrollbar=True): # show a big long string, and wait for XY to continue # - returns character used to get out (X or Y) # - can accept other chars to 'escape' as well. @@ -239,7 +240,10 @@ async def ux_show_story(msg, title=None, escape=None, sensitive=False, strict_es y += 13 - dis.scroll_bar(top / len(lines)) + if scrollbar: + # help in cases when last char in a row hidden by scroll bar + dis.scroll_bar(top / len(lines)) + dis.show() # wait to do something diff --git a/testing/test_bip39pw.py b/testing/test_bip39pw.py index b0d268f0..cc8f8f5e 100644 --- a/testing/test_bip39pw.py +++ b/testing/test_bip39pw.py @@ -12,6 +12,7 @@ from ckcc_protocol.constants import * import json from mnemonic import Mnemonic from constants import simulator_fixed_xfp, simulator_fixed_words +from helpers import xfp2str # add the BIP39 test vectors vectors = json.load(open('bip39-vectors.json'))['english'] @@ -109,6 +110,7 @@ def set_bip39_pw(dev, need_keypress, reset_seed_words, cap_story, else: need_keypress("y") # do not store + time.sleep(.2) title, story = cap_story() assert "Above is the master key fingerprint" in story @@ -236,6 +238,8 @@ 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) @@ -258,31 +262,39 @@ def test_bip39pass_on_ephemeral_seed(generate_ephemeral_words, import_ephemeral_ enter_complex(passphrase) pick_menu_item("APPLY") time.sleep(.1) - title, story = cap_story() - # title is xfp = simulator fixed words + pass (as first iteration is always from main seed) - xfp0 = title[1:-1] - seed0 = Mnemonic.to_seed(simulator_fixed_words, passphrase=passphrase) - expect0 = BIP32Node.from_master_secret(seed0) - assert expect0.fingerprint().hex().upper() == xfp0 - assert "press (2) to add passphrase to the current active temporary seed" in story + 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") - time.sleep(.5) - title, story = cap_story() - xfp1 = title[1:-1] - seed1 = Mnemonic.to_seed(" ".join(sec), passphrase=passphrase) - expect1 = BIP32Node.from_master_secret(seed1) - assert expect1.fingerprint().hex().upper() == xfp1 - assert "press (2)" not in story - need_keypress("y") else: - need_keypress("y") + 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: - to_check = xfp1 + assert tmp_fp == title_xfp + assert f"current active temporary seed [{parent_fp}]" in story else: - to_check = xfp0 + assert master_fp == title_xfp + assert f"master seed [{sim_fp}]" in story + + need_keypress("y") time.sleep(.3) title, story = cap_story() @@ -292,7 +304,7 @@ def test_bip39pass_on_ephemeral_seed(generate_ephemeral_words, import_ephemeral_ time.sleep(.1) title, story = cap_story() assert "Saved to Seed Vault" in story - assert to_check in story + assert title_xfp in story need_keypress("y") else: @@ -303,7 +315,7 @@ def test_bip39pass_on_ephemeral_seed(generate_ephemeral_words, import_ephemeral_ pick_menu_item("Seed Vault") m = cap_menu() for i in m: - if to_check in i: + if title_xfp in i: pick_menu_item(i) break else: @@ -313,7 +325,7 @@ def test_bip39pass_on_ephemeral_seed(generate_ephemeral_words, import_ephemeral_ need_keypress("y") time.sleep(.1) _, story = cap_story() - assert to_check in story + assert title_xfp in story if on_eph: assert ("BIP-39 Passphrase on [%s]" % parent_fp) in story else: