Never store 'seeds' in ephemeral settings
This commit is contained in:
parent
1a761d0daf
commit
e1ac204e04
@ -69,12 +69,10 @@ from version import mk_num, is_devmode
|
||||
|
||||
# settings linked to seed
|
||||
# LINKED_SETTINGS = ["multisig", "tp", "ovc", "xfp", "xpub", "words"]
|
||||
# settings that does not make sense to copy to ephemeral secret
|
||||
# settings that does not make sense to copy to temporary secret
|
||||
# LINKED_SETTINGS += ["sd2fa", "usr", "axi", "hsmcmd"]
|
||||
# prelogin settings - do not need to be part of other saved settings
|
||||
# PRELOGIN_SETTINGS = ["_skip_pin", "nick", "rngk", "lgto", "kbtn", "terms_ok"]
|
||||
# seed vault is singleton
|
||||
SINGLE_SETTINGS = ["seedvault", "seeds"]
|
||||
# keep these settings only if unspecified on the other end
|
||||
KEEP_IF_BLANK_SETTINGS = ["bkpw", "wa", "sighshchk", "emu", "rz",
|
||||
"axskip", "del", "pms", "idle_to", "b39skip"]
|
||||
@ -90,6 +88,31 @@ def MK4_FILENAME(slot):
|
||||
return MK4_WORKDIR + ('%03x.aes' % slot)
|
||||
|
||||
|
||||
# master current dict
|
||||
master_sv_data = None
|
||||
master_nvram_key = None
|
||||
|
||||
|
||||
def extract_master_sv_data(settings_dict):
|
||||
# allows us to specify what we want to keep
|
||||
# in global 'master_sv_data' variable
|
||||
# and save RAM
|
||||
if settings_dict is None:
|
||||
return
|
||||
seeds = settings_dict.get("seeds", [])
|
||||
seedvault = settings_dict.get("seedvault", 0)
|
||||
if (not seedvault) and (not seeds):
|
||||
return
|
||||
return {"seeds": seeds, "seedvault": seedvault,
|
||||
"xfp": settings_dict.get("xfp", 0)}
|
||||
|
||||
|
||||
def set_master_sv_data(data, key):
|
||||
global master_sv_data, master_nvram_key
|
||||
master_sv_data = extract_master_sv_data(data)
|
||||
master_nvram_key = key
|
||||
|
||||
|
||||
class SettingsObject:
|
||||
|
||||
def __init__(self, dis=None):
|
||||
@ -306,6 +329,9 @@ class SettingsObject:
|
||||
self.current['chain'] = 'XTN'
|
||||
|
||||
def get(self, kn, default=None):
|
||||
if master_sv_data and kn in ["seeds", "seedvault"]:
|
||||
# we are in temporary mode as global 'master_sv_data' is populated
|
||||
return master_sv_data.get(kn, default)
|
||||
return self.current.get(kn, default)
|
||||
|
||||
def changed(self):
|
||||
@ -330,12 +356,6 @@ class SettingsObject:
|
||||
|
||||
def merge_previous_active(self, previous):
|
||||
if previous:
|
||||
for k in SINGLE_SETTINGS:
|
||||
if k not in previous:
|
||||
self.current.pop(k, None)
|
||||
else:
|
||||
self.current[k] = previous[k]
|
||||
|
||||
for k in KEEP_IF_BLANK_SETTINGS:
|
||||
if previous.get(k, None) and not self.current.get(k, None):
|
||||
self.current[k] = previous[k]
|
||||
|
||||
@ -416,6 +416,7 @@ class PinAttempt:
|
||||
# Main secret has changed: reset the settings+their key,
|
||||
# and capture xfp/xpub
|
||||
# if None is provided as raw_secret -> restore to main seed
|
||||
import nvstore
|
||||
from glob import settings
|
||||
stash.SensitiveValues.clear_cache()
|
||||
|
||||
@ -447,9 +448,10 @@ class PinAttempt:
|
||||
sv.chain = chain
|
||||
if raw_secret is None:
|
||||
# restore to main wallet
|
||||
nv = sv.encoded_secret()
|
||||
settings.set_key(nv)
|
||||
settings.nvram_key = nvstore.master_nvram_key
|
||||
settings.load()
|
||||
# blank global as we return to main seed
|
||||
nvstore.set_master_sv_data(None, None)
|
||||
else:
|
||||
sv.capture_xpub()
|
||||
except stash.ZeroSecretException:
|
||||
@ -461,15 +463,22 @@ class PinAttempt:
|
||||
self.state_flags |= (1 << 4)
|
||||
return
|
||||
|
||||
|
||||
settings.merge_previous_active(old_values)
|
||||
|
||||
def tmp_secret(self, encoded, chain=None, bip39pw=''):
|
||||
# Use indicated secret and stop using the SE; operate like this until reboot
|
||||
val = bytes(encoded + bytes(AE_SECRET_LEN - len(encoded)))
|
||||
if self.tmp_value == val:
|
||||
# noop - already enabled
|
||||
return False
|
||||
|
||||
if not self.tmp_value:
|
||||
# going from master seed
|
||||
import nvstore
|
||||
from glob import settings
|
||||
nvstore.set_master_sv_data(settings.current,
|
||||
settings.nvram_key)
|
||||
|
||||
self.tmp_value = val
|
||||
|
||||
# We're no longer blank. hard to say about duress secret and stuff tho
|
||||
|
||||
120
shared/seed.py
120
shared/seed.py
@ -416,6 +416,8 @@ async def in_seed_vault(xfp=None, seeds=None):
|
||||
return False
|
||||
|
||||
async def add_seed_to_vault(encoded, meta=None):
|
||||
import nvstore
|
||||
|
||||
if not settings.get("seedvault", False):
|
||||
# seed vault disabled
|
||||
return
|
||||
@ -426,24 +428,21 @@ async def add_seed_to_vault(encoded, meta=None):
|
||||
_,_,node = SecretStash.decode(encoded)
|
||||
new_xfp = swab32(node.my_fp())
|
||||
new_xfp_str = xfp2str(new_xfp)
|
||||
tmp_val = None
|
||||
|
||||
# do not offer to store secrets that are already in vault
|
||||
if await in_seed_vault(new_xfp_str, settings.get("seeds", [])):
|
||||
return
|
||||
|
||||
if pa.tmp_value:
|
||||
dis.fullscreen("Wait...")
|
||||
tmp_val = pa.tmp_value[:]
|
||||
await restore_to_main_secret(preserve_settings=True)
|
||||
|
||||
# do not offer to store main seed
|
||||
if new_xfp == settings.get("xfp", 0):
|
||||
if tmp_val:
|
||||
pa.tmp_secret(tmp_val)
|
||||
if nvstore.master_sv_data:
|
||||
main_xfp = nvstore.master_sv_data.get("xfp", 0)
|
||||
else:
|
||||
main_xfp = settings.get("xfp", 0)
|
||||
|
||||
if new_xfp == main_xfp:
|
||||
return
|
||||
|
||||
# if in ephemeral 'seeds' are grabbed from master_sv_data global var
|
||||
seeds = settings.get("seeds", [])
|
||||
|
||||
xfp_ui = "[%s]" % new_xfp_str
|
||||
@ -454,26 +453,34 @@ async def add_seed_to_vault(encoded, meta=None):
|
||||
|
||||
ch = await ux_show_story(story, escape="1")
|
||||
if ch == "1":
|
||||
eph_nvram_key = None
|
||||
seeds.append((new_xfp_str,
|
||||
stash.SecretStash.storage_encode(encoded),
|
||||
xfp_ui,
|
||||
meta))
|
||||
if nvstore.master_sv_data:
|
||||
# in ephemeral
|
||||
nvstore.master_sv_data["seeds"] = seeds
|
||||
eph_nvram_key = settings.nvram_key[:]
|
||||
settings.nvram_key = nvstore.master_nvram_key
|
||||
settings.load()
|
||||
|
||||
settings.set("seeds", seeds)
|
||||
settings.save()
|
||||
|
||||
if eph_nvram_key:
|
||||
# restore ephemerlal settings after save to main settings
|
||||
settings.nvram_key = eph_nvram_key
|
||||
settings.load()
|
||||
|
||||
await ux_show_story(xfp_ui + "\nSaved to Seed Vault")
|
||||
|
||||
if tmp_val:
|
||||
dis.fullscreen("Wait...")
|
||||
pa.tmp_secret(tmp_val)
|
||||
settings.set("seeds", seeds)
|
||||
settings.save()
|
||||
|
||||
return True
|
||||
|
||||
async def set_ephemeral_seed(encoded, chain=None, summarize_ux=True, bip39pw='',
|
||||
is_restore=False, meta=None):
|
||||
if not is_restore:
|
||||
await add_seed_to_vault(encoded, meta=meta)
|
||||
dis.fullscreen("Wait...")
|
||||
|
||||
applied = pa.tmp_secret(encoded, chain=chain, bip39pw=bip39pw)
|
||||
dis.progress_bar_show(1)
|
||||
@ -551,6 +558,7 @@ async def set_seed_extended_key(extended_key):
|
||||
|
||||
async def set_ephemeral_seed_extended_key(extended_key, meta=None):
|
||||
encoded, chain = xprv_to_encoded_secret(extended_key)
|
||||
dis.fullscreen("Applying...")
|
||||
await set_ephemeral_seed(encoded=encoded, chain=chain, meta=meta)
|
||||
goto_top_menu()
|
||||
|
||||
@ -805,6 +813,7 @@ class SeedVaultMenu(MenuSystem):
|
||||
|
||||
@staticmethod
|
||||
async def _clear(menu, label, item):
|
||||
import nvstore
|
||||
from glob import dis, settings
|
||||
|
||||
idx, xfp_str, encoded = item.arg
|
||||
@ -822,13 +831,11 @@ class SeedVaultMenu(MenuSystem):
|
||||
|
||||
dis.fullscreen("Saving...")
|
||||
|
||||
tmp_val = None
|
||||
wipe_slot = (ch != "1")
|
||||
tmp_val = False
|
||||
|
||||
if pa.tmp_value:
|
||||
# active ephemeral seed
|
||||
assert (encoded == stash.SecretStash.storage_encode(pa.tmp_value))
|
||||
tmp_val = pa.tmp_value[:]
|
||||
tmp_val = True
|
||||
|
||||
if wipe_slot:
|
||||
# are we deleting current active ephemeral wallet
|
||||
@ -836,31 +843,47 @@ class SeedVaultMenu(MenuSystem):
|
||||
# slot wiping
|
||||
if tmp_val:
|
||||
# wipe current settings
|
||||
master_nvram_key = nvstore.master_nvram_key
|
||||
settings.blank()
|
||||
else:
|
||||
# in main settings
|
||||
settings.save()
|
||||
pa.tmp_secret(pad_raw_secret(encoded))
|
||||
master_nvram_key = settings.nvram_key[:]
|
||||
settings.set_key(pad_raw_secret(encoded))
|
||||
settings.load()
|
||||
settings.blank()
|
||||
|
||||
if pa.tmp_value:
|
||||
await restore_to_main_secret(preserve_settings=True)
|
||||
# back to master settings
|
||||
pa.tmp_value = False
|
||||
settings.nvram_key = master_nvram_key
|
||||
settings.load()
|
||||
nvstore.set_master_sv_data(None, None)
|
||||
|
||||
eph_nvram_key = None
|
||||
# will get global master_sv_data from nvstore if in ephemeral
|
||||
seeds = settings.get("seeds", [])
|
||||
try:
|
||||
del seeds[idx]
|
||||
settings.set("seeds", seeds)
|
||||
settings.save()
|
||||
except IndexError: pass
|
||||
finally:
|
||||
if tmp_val and (not wipe_slot):
|
||||
# we were in ephemeral mode before and have not
|
||||
# wiped seed - return back to ephemeral
|
||||
pa.tmp_secret(tmp_val)
|
||||
settings.set("seeds", seeds)
|
||||
settings.save()
|
||||
elif tmp_val and wipe_slot:
|
||||
goto_top_menu()
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
if nvstore.master_sv_data:
|
||||
# in ephemeral
|
||||
nvstore.master_sv_data["seeds"] = seeds
|
||||
eph_nvram_key = settings.nvram_key[:]
|
||||
settings.nvram_key = nvstore.master_nvram_key
|
||||
settings.load()
|
||||
|
||||
settings.set("seeds", seeds)
|
||||
settings.save()
|
||||
|
||||
if tmp_val and (not wipe_slot):
|
||||
# we were in ephemeral mode before and have not
|
||||
# wiped seed - return back to ephemeral
|
||||
settings.nvram_key = eph_nvram_key
|
||||
settings.load()
|
||||
elif tmp_val and wipe_slot:
|
||||
goto_top_menu()
|
||||
|
||||
# pop menu stack
|
||||
the_ux.pop()
|
||||
@ -881,17 +904,12 @@ class SeedVaultMenu(MenuSystem):
|
||||
@staticmethod
|
||||
async def _rename(menu, label, item):
|
||||
# let them edit the name
|
||||
import nvstore
|
||||
from glob import dis
|
||||
|
||||
idx, xfp_str = item.arg
|
||||
|
||||
tmp_val = None
|
||||
if pa.tmp_value:
|
||||
tmp_val = pa.tmp_value[:]
|
||||
dis.fullscreen("Wait...")
|
||||
settings.save()
|
||||
await restore_to_main_secret(preserve_settings=True)
|
||||
|
||||
# will get global master_sv_data from nvstore if in ephemeral
|
||||
seeds = settings.get("seeds", [])
|
||||
chk_xfp, encoded, old_name, meta = seeds[idx]
|
||||
assert chk_xfp == xfp_str
|
||||
@ -900,18 +918,26 @@ class SeedVaultMenu(MenuSystem):
|
||||
new_name = await ux_input_text(old_name, confirm_exit=False, max_len=40)
|
||||
|
||||
if not new_name:
|
||||
new_name = old_name
|
||||
return
|
||||
|
||||
dis.fullscreen("Saving...")
|
||||
|
||||
# save it
|
||||
eph_nvram_key = None
|
||||
seeds[idx] = (chk_xfp, encoded, new_name, meta)
|
||||
if nvstore.master_sv_data:
|
||||
# in ephemeral
|
||||
nvstore.master_sv_data["seeds"] = seeds
|
||||
eph_nvram_key = settings.nvram_key[:]
|
||||
settings.nvram_key = nvstore.master_nvram_key
|
||||
settings.load()
|
||||
|
||||
settings.set("seeds", seeds)
|
||||
settings.save()
|
||||
if tmp_val:
|
||||
pa.tmp_secret(tmp_val)
|
||||
settings.set("seeds", seeds)
|
||||
settings.save()
|
||||
if eph_nvram_key:
|
||||
# restore ephemerlal settings after save to main settings
|
||||
settings.nvram_key = eph_nvram_key
|
||||
settings.load()
|
||||
|
||||
# update label in sub-menu
|
||||
menu.items[0].label = new_name
|
||||
|
||||
@ -9,6 +9,7 @@ import stash
|
||||
from seed import set_seed_value
|
||||
from utils import xfp2str
|
||||
from actions import goto_top_menu
|
||||
import nvstore
|
||||
|
||||
tn = chains.BitcoinTestnet
|
||||
|
||||
@ -21,6 +22,8 @@ settings.set('idle_to', 0)
|
||||
|
||||
import main
|
||||
pa.tmp_value = None
|
||||
nvstore.master_sv_data = None
|
||||
nvstore.master_nvram_key = None
|
||||
set_seed_value(main.WORDS)
|
||||
|
||||
print("New key in effect: %s" % settings.get('xpub', 'MISSING'))
|
||||
|
||||
@ -750,7 +750,7 @@ def test_activate_current_tmp_secret(reset_seed_words, goto_eph_seed_menu,
|
||||
])
|
||||
def test_seed_vault_menus(dev, data, settings_set, settings_get, pick_menu_item, need_keypress, cap_story,
|
||||
cap_menu, reset_seed_words, get_identity_story, get_seed_value_ux, fake_txn,
|
||||
try_sign, sim_exec, goto_home, goto_eph_seed_menu):
|
||||
try_sign, sim_exec, goto_home):
|
||||
# Verify "seed vault" feature works as intended
|
||||
reset_seed_words()
|
||||
xfp, entropy, mnemonic = data
|
||||
@ -775,8 +775,8 @@ def test_seed_vault_menus(dev, data, settings_set, settings_get, pick_menu_item,
|
||||
pick_menu_item("Seed Vault")
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
assert "Enable Seed Vault?" in story
|
||||
need_keypress("y")
|
||||
if "Enable Seed Vault?" in story:
|
||||
need_keypress("y")
|
||||
time.sleep(.1)
|
||||
pick_menu_item("Enable")
|
||||
time.sleep(.5)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user