bugfix: do not allow to import master seed as temporary

This commit is contained in:
scgbckbone 2023-12-07 14:47:47 +01:00
parent 2ca125ab07
commit d4f13317fc
4 changed files with 131 additions and 23 deletions

View File

@ -116,12 +116,25 @@ class SettingsObject:
ctr = ustruct.pack('<4I', 4, 3, 2, pos)
return aes256ctr.new(self.nvram_key, ctr)
@staticmethod
def hash_key(secret):
# hash up the secret... without decoding it or similar
assert len(secret) >= 32
s = sha256(secret)
for round in range(5):
s.update('pad')
s = sha256(s.digest())
return s.digest()
def set_key(self, new_secret=None):
# System settings (not secrets) are stored in flash, encrypted with this
# key that is derived from main wallet secret. Call this method when the secret
# is first loaded, or changes for some reason.
from pincodes import pa
from stash import blank_object, SensitiveValues
from stash import blank_object
key = None
mine = False
@ -136,16 +149,7 @@ class SettingsObject:
mine = True
if new_secret:
# hash up the secret... without decoding it or similar
assert len(new_secret) >= 32
s = sha256(new_secret)
for round in range(5):
s.update('pad')
s = sha256(s.digest())
key = s.digest()
key = self.hash_key(new_secret)
if mine:
blank_object(new_secret)

View File

@ -412,7 +412,8 @@ class PinAttempt:
self.roundtrip(7, fw_upgrade=(start, length))
# not-reached
def new_main_secret(self, raw_secret=None, chain=None, bip39pw='', blank=False):
def new_main_secret(self, raw_secret=None, chain=None, bip39pw='', blank=False,
target_nvram_key = None):
# Main secret has changed: reset the settings+their key,
# and capture xfp/xpub
# if None is provided as raw_secret -> restore to main seed
@ -437,7 +438,13 @@ class PinAttempt:
settings.blank()
old_values = None
else:
settings.set_key(raw_secret)
if target_nvram_key is None:
settings.set_key(raw_secret)
else:
# we already have hashed nvram key calculated
# from self.tmp_secret - use it
settings.nvram_key = target_nvram_key
settings.load()
# Recalculate xfp/xpub values (depends both on secret and chain)
@ -465,17 +472,30 @@ class PinAttempt:
settings.load()
self.state_flags |= PA_ZERO_SECRET
def tmp_secret(self, encoded, chain=None, bip39pw=''):
# Use indicated secret and stop using the SE; operate like this until reboot
from glob import settings
val = bytes(encoded + bytes(AE_SECRET_LEN - len(encoded)))
if self.tmp_value == val:
# noop - already enabled
return False
return False, "Temporary master key already in use."
target_nvram_key = None
if encoded is not None:
# disallow using master seed as temporary
master_err = "Cannot use master seed as temporary."
target_nvram_key = settings.hash_key(encoded)
if settings.master_nvram_key:
assert self.tmp_value
if target_nvram_key == settings.master_nvram_key:
return False, master_err
else:
if target_nvram_key == settings.nvram_key:
return False, master_err
if not self.tmp_value:
# leaving from master seed, might capture some useful values
from glob import settings
settings.leaving_master_seed()
self.tmp_value = val
@ -485,13 +505,14 @@ class PinAttempt:
# Copies system settings to new encrypted-key value, calculates
# XFP, XPUB and saves into that, and starts using them.
self.new_main_secret(self.tmp_value, chain=chain, bip39pw=bip39pw)
self.new_main_secret(self.tmp_value, chain=chain, bip39pw=bip39pw,
target_nvram_key=target_nvram_key)
# On Q1, update status icons
from glob import dis
dis.draw_status(bip39=1 if bip39pw else 0, tmp=1)
return True
return True, None
def trick_request(self, method_num, data):
# send/recv a trick-pin related request (mk4 only)

View File

@ -466,13 +466,13 @@ async def set_ephemeral_seed(encoded, chain=None, summarize_ux=True, bip39pw='',
await add_seed_to_vault(encoded, meta=meta)
dis.fullscreen("Wait...")
applied = pa.tmp_secret(encoded, chain=chain, bip39pw=bip39pw)
applied, err_msg = pa.tmp_secret(encoded, chain=chain, bip39pw=bip39pw)
dis.progress_bar_show(1)
xfp = settings.get("xfp", None)
if xfp:
xfp = "[" + xfp2str(xfp) + "]"
if not applied:
await ux_show_story(title=xfp, msg="Temporary master key already in use.")
await ux_show_story(title="FAILED", msg=err_msg)
return
if summarize_ux:

View File

@ -4,7 +4,8 @@
#
import pytest, time, re, os, shutil, pdb, hashlib
from constants import simulator_fixed_tpub, simulator_fixed_words, simulator_fixed_xfp, simulator_fixed_xpub
from constants import simulator_fixed_tpub, simulator_fixed_xfp, simulator_fixed_xpub
from constants import simulator_fixed_words, simulator_fixed_tprv
from ckcc.protocol import CCProtocolPacker
from txn import fake_txn
from test_ux import word_menu_entry
@ -763,8 +764,8 @@ def test_activate_current_tmp_secret(reset_seed_words, goto_eph_seed_menu,
title, story = cap_story()
assert "Temporary master key already in use" in story
already_used_xfp = title[1:-1]
assert already_used_xfp == in_effect_xfp == expected_xfp
assert title == "FAILED"
assert in_effect_xfp == expected_xfp
need_keypress("y")
@ -1280,4 +1281,86 @@ def test_temporary_from_backup(multisig, backup_system, import_ms_wallet, get_se
else:
restore_main_seed(False)
def test_import_master_as_tmp(reset_seed_words, goto_eph_seed_menu, cap_story,
ephemeral_seed_disabled, pick_menu_item, goto_home,
need_keypress, word_menu_entry, settings_set,
confirm_tmp_seed, cap_menu, microsd_path,
restore_main_seed, get_identity_story):
reset_seed_words()
goto_eph_seed_menu()
ephemeral_seed_disabled()
# try import same seed as current simulator master
words, expected_xfp = simulator_fixed_words, simulator_fixed_xfp
xfp_str = xfp2str(expected_xfp)
pick_menu_item("Import Words")
pick_menu_item(f"24 Words")
time.sleep(0.1)
word_menu_entry(words.split())
time.sleep(.1)
title, story = cap_story()
assert "FAILED" == title
assert 'Cannot use master seed as temporary.' in story
need_keypress("x")
# go to ephemeral seed and then try to create new ephemeral seed from master
# when in different temporary seed whatsoever
goto_eph_seed_menu()
# random temporary seed
pick_menu_item("Generate Words")
pick_menu_item(f"12 Words")
need_keypress("6") # skip quiz
need_keypress("y") # yes - I'm sure
confirm_tmp_seed(seedvault=False)
goto_home()
time.sleep(0.1)
menu = cap_menu()
# ephemeral seed chosen
assert "[" in menu[0]
goto_eph_seed_menu()
pick_menu_item("Import Words")
pick_menu_item(f"24 Words")
time.sleep(0.1)
word_menu_entry(words.split())
time.sleep(.1)
title, story = cap_story()
assert "FAILED" == title
assert 'Cannot use master seed as temporary.' in story
need_keypress("x")
# now import same seed but represented as master extended key
# this works and does not delete master settings as encoded
# secret is different and therefore nvram_key too
fname = "ek_sim.txt"
with open(microsd_path(fname), "w") as f:
f.write(simulator_fixed_tprv)
goto_eph_seed_menu()
pick_menu_item("Import XPRV")
title, story = cap_story()
if "Press (1)" in story:
need_keypress("1")
need_keypress("y") # Select file containing...
pick_menu_item(fname)
confirm_tmp_seed(seedvault=False) # allowed
# verify we are in temporary seed
goto_home()
time.sleep(0.1)
menu = cap_menu()
# ephemeral seed chosen
assert "[" in menu[0]
assert xfp_str in menu[0]
restore_main_seed(preserve_settings=False, seed_vault=False)
story = get_identity_story()
assert "00000000" not in story
assert xfp_str in story
# EOF