diff --git a/shared/actions.py b/shared/actions.py index 3791c192..416cdc71 100644 --- a/shared/actions.py +++ b/shared/actions.py @@ -934,7 +934,7 @@ async def restore_main_secret(*a): escape = None msg = "Restore main wallet and its settings?\n\n" - if not in_seed_vault(): + if not in_seed_vault(pa.tmp_value): msg += ( "Press OK to forget current temporary wallet " "settings, or press (1) to save & keep " diff --git a/shared/seed.py b/shared/seed.py index b446db5f..482e6ee7 100644 --- a/shared/seed.py +++ b/shared/seed.py @@ -406,17 +406,13 @@ async def new_from_dice(nwords): # send them to home menu, now with a wallet enabled goto_top_menu(first_time=True) -def in_seed_vault(xfp=None): +def in_seed_vault(encoded): # Test if indicated xfp (or currently active XFP) is in the seed vault already. - - if xfp is None: - xfp = xfp2str(settings.get("xfp", 0)) - seeds = settings.master_get("seeds", []) - - if seeds and (xfp in [s[0] for s in seeds]): - return True - + if seeds: + ss = stash.SecretStash.storage_serialize(encoded) + if ss in [s[1] for s in seeds]: + return True return False async def add_seed_to_vault(encoded, meta=None): @@ -433,7 +429,7 @@ async def add_seed_to_vault(encoded, meta=None): new_xfp_str = xfp2str(new_xfp) # do not offer to store secrets that are already in vault - if in_seed_vault(new_xfp_str): + if in_seed_vault(encoded): return # do not offer to store main seed @@ -796,9 +792,8 @@ class SeedVaultMenu(MenuSystem): dis.fullscreen("Applying...") xfp, encoded = item.arg - raw = pad_raw_secret(encoded) - await set_ephemeral_seed(raw, is_restore=True) + await set_ephemeral_seed(encoded, is_restore=True) goto_top_menu() @@ -838,7 +833,7 @@ class SeedVaultMenu(MenuSystem): else: # in main settings xs = SettingsObject() - xs.set_key(pad_raw_secret(encoded)) + xs.set_key(encoded) xs.load() xs.blank() del xs @@ -866,7 +861,7 @@ class SeedVaultMenu(MenuSystem): xfp_str, encoded, name, meta = item.arg # - first byte represents type of secret (internal encoding flag) - txt = SecretStash.summary(a2b_hex(encoded[0:2])[0]) + txt = SecretStash.summary(encoded[0]) detail = "Name:\n%s\n\nMaster XFP:\n%s\n\nOrigin:\n%s\n\nSecret Type:\n%s" \ % (name, xfp_str, meta, txt) @@ -923,7 +918,8 @@ class SeedVaultMenu(MenuSystem): rv.append(MenuItem("Temporary Seed", menu=make_ephemeral_seed_menu)) else: for i, (xfp_str, encoded, name, meta) in enumerate(seeds): - is_active = (cur_xfp == xfp_str) + encoded = pad_raw_secret(encoded) + is_active = (encoded == pa.tmp_value) submenu = [ MenuItem(name, f=cls._detail, arg=(xfp_str, encoded, name, meta)), MenuItem('Use This Seed', f=cls._set, arg=(xfp_str, encoded)), diff --git a/testing/test_ephemeral.py b/testing/test_ephemeral.py index a9d51221..d808b344 100644 --- a/testing/test_ephemeral.py +++ b/testing/test_ephemeral.py @@ -2,7 +2,8 @@ # # Ephemeral Seeds tests # -import pytest, time, re, os, shutil, pdb + +import pytest, time, re, os, shutil, pdb, hashlib from constants import simulator_fixed_tpub, simulator_fixed_words, simulator_fixed_xfp, simulator_fixed_xpub from ckcc.protocol import CCProtocolPacker from txn import fake_txn @@ -367,18 +368,19 @@ def import_ephemeral_xprv(microsd_path, virtdisk_path, goto_eph_seed_menu, if extended_key is None: node = BIP32Node.from_master_secret(os.urandom(32), netcode=netcode) ek = node.hwif(as_private=True) + '\n' - if way == "sd": - fpath = microsd_path(fname) - elif way == "vdisk": - fpath = virtdisk_path(fname) - if way != "nfc": - with open(fpath, "w") as f: - f.write(ek) else: node = BIP32Node.from_wallet_key(extended_key) assert extended_key == node.hwif(as_private=True) ek = extended_key + if way == "sd": + fpath = microsd_path(fname) + elif way == "vdisk": + fpath = virtdisk_path(fname) + if way != "nfc": + with open(fpath, "w") as f: + f.write(ek) + if testnet: assert "tprv" in ek else: @@ -1117,4 +1119,49 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item # still in ephemeral assert title == m[0] + +def test_xfp_collision(reset_seed_words, settings_set, import_ephemeral_xprv, + cap_story, need_keypress, pick_menu_item, cap_menu): + node = BIP32Node.from_master_secret(os.urandom(32), netcode="XTN") + xfp = node.fingerprint().hex().upper() + k0 = node.hwif(as_private=True) + + # change chain code but presevre public key + node._chain_code = hashlib.sha256(node._chain_code).digest() + k1 = node.hwif(as_private=True) + assert k1 != k0 + + reset_seed_words() + settings_set("seedvault", 1) + settings_set("seeds", []) + + import_ephemeral_xprv("sd", extended_key=k0, seed_vault=True, from_main=True) + title, story = cap_story() + assert "is in effect now" in story + need_keypress("y") + + import_ephemeral_xprv("sd", extended_key=k1, seed_vault=True, from_main=False) + title, story = cap_story() + assert "is in effect now" in story + need_keypress("y") + + pick_menu_item("Seed Vault") + m = cap_menu() + assert len(m) == 3 # two seeds and Restore Master + # same master fingerprints + assert xfp in m[0] + assert xfp in m[1] + # but only second is in use + pick_menu_item(m[1]) + time.sleep(.1) + sm = cap_menu() + assert "Seed In Use" in sm + assert "Use This Seed" not in sm + need_keypress("x") # go back + pick_menu_item(m[0]) + time.sleep(.1) + sm = cap_menu() + assert "Seed In Use" not in sm + assert "Use This Seed" in sm + # EOF