bugfix: do not allow to import master seed as temporary
This commit is contained in:
parent
f8ac8eda89
commit
9188c7faf2
@ -12,6 +12,7 @@
|
||||
- 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
|
||||
- Bugfix: Disallow using master seed as temporary seed
|
||||
|
||||
## 5.2.0 - 2023-10-10
|
||||
|
||||
|
||||
@ -115,12 +115,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,15 +149,7 @@ class SettingsObject:
|
||||
|
||||
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)
|
||||
|
||||
@ -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
|
||||
@ -438,7 +439,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)
|
||||
@ -462,17 +469,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
|
||||
@ -482,9 +502,10 @@ 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)
|
||||
|
||||
return True
|
||||
return True, None
|
||||
|
||||
def trick_request(self, method_num, data):
|
||||
# send/recv a trick-pin related request (mk4 only)
|
||||
|
||||
@ -471,15 +471,16 @@ 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
|
||||
|
||||
|
||||
xfp = "[" + xfp2str(settings.get("xfp", 0)) + "]"
|
||||
if summarize_ux:
|
||||
await ux_show_story(title=xfp, msg="New temporary master key is in effect now.")
|
||||
|
||||
|
||||
@ -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")
|
||||
|
||||
|
||||
@ -1317,4 +1318,86 @@ def test_tmp_upgrade_disabled(reset_seed_words, need_keypress, pick_menu_item,
|
||||
m = cap_menu()
|
||||
assert "Upgrade Firmware" not in m
|
||||
|
||||
|
||||
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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user