From fe63163c855c4420050a771aefc628e9cd8a2b7f Mon Sep 17 00:00:00 2001 From: scgbckbone Date: Fri, 11 Aug 2023 17:04:05 +0200 Subject: [PATCH] restore to main se2 secret without reboot; active ephemeral seeds have first home menu item [XFP]; tests reorg - created separate test_backup.py; add ability to remove ephemeral seed settings via Restore Seed --- releases/ChangeLog.md | 1 + shared/actions.py | 78 ++++---- shared/backups.py | 14 +- shared/nvstore.py | 5 +- shared/seed.py | 24 ++- shared/stash.py | 9 + testing/conftest.py | 14 ++ testing/devtest/backups.py | 9 +- testing/run_sim_tests.py | 6 +- testing/test_backup.py | 381 +++++++++++++++++++++++++++++++++++++ testing/test_bip39pw.py | 71 +------ testing/test_ephemeral.py | 184 ++++++++---------- testing/test_se2.py | 76 -------- testing/test_sign.py | 6 +- testing/test_ux.py | 151 --------------- unix/sim_boot.py | 11 +- unix/variant/sim_se2.py | 8 +- 17 files changed, 583 insertions(+), 465 deletions(-) create mode 100644 testing/test_backup.py diff --git a/releases/ChangeLog.md b/releases/ChangeLog.md index 49df719b..feb28953 100644 --- a/releases/ChangeLog.md +++ b/releases/ChangeLog.md @@ -4,6 +4,7 @@ - New Feature: `Lock Down Seed` now works with every ephemeral secret (not just BIP39 passphrase) - New Feature: BIP-39 Passphrase can now be added to word based Ephemeral Seeds - New Feature: Add ability to back-up BIP39 Passphrase wallet +- New Feature: Restore to main secret from ephemeral without need to reboot the device - Enhancement: Shortcut to `Batch Sign PSBT` via `Ready To Sign` -> `Press (9)` - Enhancement: Old plausible deniability feature on fresh COLDCARD removed. Only needed for Mk 2-3 where SPI flash was external chip, diff --git a/shared/actions.py b/shared/actions.py index 92ef3f8a..a178a12f 100644 --- a/shared/actions.py +++ b/shared/actions.py @@ -792,15 +792,6 @@ async def start_login_sequence(): from glob import dis import callgate - if version.mk_num < 4: - # Block very obsolete versions. - try: - MIN_WATERMARK = b'!\x03)\x19\'"\x00\x00' # b2a_hex('2103291927220000') - now = callgate.get_highwater() - if now < MIN_WATERMARK: - callgate.set_highwater(MIN_WATERMARK) - except: pass - if pa.is_blank(): # Blank devices, with no PIN set all, can continue w/o login goto_top_menu() @@ -924,26 +915,40 @@ async def start_login_sequence(): await ar.interact() except: pass - if version.mk_num >= 4: - if version.has_nfc and settings.get('nfc', 0): - # Maybe allow NFC now - import nfc - nfc.NFCHandler.startup() + if version.has_nfc and settings.get('nfc', 0): + # Maybe allow NFC now + import nfc + nfc.NFCHandler.startup() - if settings.get('vidsk', 0): - # Maybe start virtual disk - import vdisk - vdisk.VirtDisk() + if settings.get('vidsk', 0): + # Maybe start virtual disk + import vdisk + vdisk.VirtDisk() # Allow USB protocol, now that we are auth'ed if not settings.get('du', 0): from usb import enable_usb enable_usb() +async def restore_main_secret(*a): + ch = await ux_show_story( + "Restore main wallet and its settings?\n\n" + "Press OK to forget current ephemeral wallet " + "settings, or press (1) to save & keep " + "those settings for later use.", + escape="1" + ) + if ch == "x": return + + from seed import restore_to_main_secret + await restore_to_main_secret(False if ch == "y" else True) + goto_top_menu() + def make_top_menu(): - from menu import MenuSystem + from menu import MenuSystem, MenuItem from flow import VirginSystem, NormalSystem, EmptyWallet, FactoryMenu - from glob import hsm_active + from glob import hsm_active, settings + from pincodes import pa if hsm_active: from hsm_ux import hsm_ux_obj @@ -956,7 +961,18 @@ def make_top_menu(): else: assert pa.is_successful(), "nonblank but wrong pin" - m = MenuSystem(EmptyWallet if pa.is_secret_blank() else NormalSystem) + if not pa.is_secret_blank(): + _cls = NormalSystem[:] + if pa.tmp_value: + active_xfp = settings.get("xfp", 0) + if active_xfp: + ui_xfp = "[" + xfp2str(active_xfp) + "]" + _cls.insert(0, MenuItem(ui_xfp, f=ready2sign)) + _cls.append(MenuItem("Restore Seed", f=restore_main_secret)) + else: + _cls = EmptyWallet + + m = MenuSystem(_cls) return m def goto_top_menu(first_time=False): @@ -1976,12 +1992,11 @@ We strongly recommend all PIN codes used be unique between each other. title="Try Again") continue - if version.mk_num >= 4: - from trick_pins import tp - prob = tp.check_new_main_pin(pin) - if prob: - await ux_show_story(prob, title="Try Again") - continue + from trick_pins import tp + prob = tp.check_new_main_pin(pin) + if prob: + await ux_show_story(prob, title="Try Again") + continue break @@ -2018,10 +2033,9 @@ We strongly recommend all PIN codes used be unique between each other. # we cannot/need not login again pa.login() - if version.mk_num >= 4: - # Deltamode trick pins need to track main pin - from trick_pins import tp - tp.main_pin_has_changed(pa.pin.decode()) + # Deltamode trick pins need to track main pin + from trick_pins import tp + tp.main_pin_has_changed(pa.pin.decode()) if mode == 'duress': # program the duress secret now... it's derived from real wallet contents @@ -2062,7 +2076,7 @@ async def show_version(*a): serial += '\n\nNFC UID:\n' + NFC.get_uid().replace(':', '') hw = version.hw_label - if not version.has_nfc and version.mk_num >= 4: + if not version.has_nfc: hw += ' (no NFC)' msg = '''\ diff --git a/shared/backups.py b/shared/backups.py index 6f43945a..6b11aca7 100644 --- a/shared/backups.py +++ b/shared/backups.py @@ -199,12 +199,14 @@ def restore_from_dict_ll(vals): if k == 'tp': # restore trick pins, which may involve many ops - if version.mk_num >= 4: - from trick_pins import tp - try: - tp.restore_backup(vals[key]) - except Exception as exc: - sys.print_exception(exc) + from trick_pins import tp + try: + tp.restore_backup(vals[key]) + except Exception as exc: + sys.print_exception(exc) + + # continue as `tp.restore_backup` handles + # saving into settings continue settings.set(k, vals[key]) diff --git a/shared/nvstore.py b/shared/nvstore.py index 5a9279a5..00ce989e 100644 --- a/shared/nvstore.py +++ b/shared/nvstore.py @@ -400,7 +400,7 @@ class SettingsObject: self.my_pos = pos self.is_dirty = 0 - def blank(self): + def blank(self, blank_current=True): # erase current copy of values in nvram; older ones may exist still # - use when clearing the seed value if self.my_pos is not None: @@ -408,7 +408,8 @@ class SettingsObject: self.my_pos = 0 # act blank too, just in case. - self.current.clear() + if blank_current: + self.current.clear() self.is_dirty = 0 self.capacity = 0 diff --git a/shared/seed.py b/shared/seed.py index b8b8499d..68875ebf 100644 --- a/shared/seed.py +++ b/shared/seed.py @@ -607,11 +607,7 @@ async def remember_ephemeral_seed(): dis.fullscreen('Check...') with stash.SensitiveValues() as sv: - if sv.mode == "xprv": - nv = SecretStash.encode(xprv=sv.node) - else: - assert sv.mode == "words" - nv = SecretStash.encode(seed_phrase=sv.raw) + nv = sv.encoded_secret() dis.fullscreen('Saving...') pa.change(new_secret=nv) @@ -624,6 +620,23 @@ async def remember_ephemeral_seed(): pa.reset() pa.login() +async def restore_to_main_secret(preserve_settings=False): + # go back to main se2 secret + import stash + from glob import settings + + # get main secret + with stash.SensitiveValues(bypass_tmp=True) as sv: + nv = sv.encoded_secret() + # drop ephemeral secret + pa.tmp_value = None + if not preserve_settings: + # we do not blank ram as we want to merge + # settings that we want to keep KEEP_SETTINGS (nvstore.py) + settings.blank(blank_current=False) + + pa.new_main_secret(nv) + def clear_seed(): from glob import dis import utime, callgate @@ -752,6 +765,7 @@ class EphemeralSeedMenu(MenuSystem): return rv + async def make_ephemeral_seed_menu(*a): if not pa.tmp_value: # force a warning on them, unless they are already doing it. diff --git a/shared/stash.py b/shared/stash.py index 0a97aee2..80061194 100644 --- a/shared/stash.py +++ b/shared/stash.py @@ -350,4 +350,13 @@ class SensitiveValues: self.register(pk) return pk + def encoded_secret(self): + # we do not support master as secret - only extended keys and mnemonics + if self.mode == "xprv": + nv = SecretStash.encode(xprv=self.node) + else: + assert self.mode == "words" + nv = SecretStash.encode(seed_phrase=self.raw) + return nv + # EOF diff --git a/testing/conftest.py b/testing/conftest.py index 0ea8e2db..04f09785 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -571,6 +571,9 @@ def goto_home(cap_menu, need_keypress, pick_menu_item): if m[0] in { 'New Seed Words', 'Ready To Sign'}: break + if (m[1] == "Ready To Sign") and m[0][0] == "[": + # ephemeral has XFP as first menu item + break else: raise pytest.fail("trapped in a menu") @@ -674,6 +677,16 @@ def open_microsd(simulator, microsd_path): return doit +@pytest.fixture(scope='module') +def settings_path(simulator): + # open a file from the simulated microsd + + def doit(fn): + # could use: ckcc.get_sim_root_dirs() here + return '../unix/work/settings/' + fn + + return doit + @pytest.fixture(scope="function") def set_master_key(sim_exec, sim_execfile, simulator, reset_seed_words): # load simulator w/ a specific bip32 master key @@ -1752,5 +1765,6 @@ from test_bip39pw import set_bip39_pw from test_ephemeral import generate_ephemeral_words, import_ephemeral_xprv, goto_eph_seed_menu from test_ephemeral import ephemeral_seed_disabled_ui from test_ux import enter_complex, pass_word_quiz, word_menu_entry +from test_se2 import goto_trick_menu, clear_all_tricks, new_trick_pin, se2_gate, new_pin_confirmed # EOF diff --git a/testing/devtest/backups.py b/testing/devtest/backups.py index 5d93f462..754ba723 100644 --- a/testing/devtest/backups.py +++ b/testing/devtest/backups.py @@ -129,13 +129,8 @@ print("done") # test recovery/reset -if version.mk_num <= 3: - from sflash import SF - SF.chip_erase() - settings.load() -else: - settings.clear() - settings.save() +settings.clear() +settings.save() print("fully done") diff --git a/testing/run_sim_tests.py b/testing/run_sim_tests.py index 186b17f2..815c29c0 100644 --- a/testing/run_sim_tests.py +++ b/testing/run_sim_tests.py @@ -238,16 +238,18 @@ def main(): continue print("Started", test_module) if test_module in ["test_bsms.py", "test_address_explorer.py", "test_export.py", - "test_multisig.py", "test_ephemeral.py", "test_ux.py"]: + "test_multisig.py", "test_ux.py"]: test_args = DEFAULT_SIMULATOR_ARGS + ["--set", "vidsk=1"] if test_module == "test_vdisk.py": test_args = ["--eject"] + DEFAULT_SIMULATOR_ARGS + ["--set", "vidsk=1"] if test_module == "test_bip39pw.py": test_args = [] - if test_module in ["test_unit.py", "test_se2.py"]: + if test_module in ["test_unit.py", "test_se2.py", "test_backup.py"]: # test_nvram_mk4 needs to run without --eff # se2 duress wallet activated as ephemeral seed requires proper `settings.load` test_args = ["--set", "nfc=1"] + if test_module == "test_ephemeral.py": + test_args = ["--set", "nfc=1", "--set", "vidsk=1"] ec, failed_tests = run_tests_with_simulator(test_module, simulator_args=test_args, pytest_k=args.pytest_k, pdb=args.pdb, failed_first=args.ff, psbt2=args.psbt2) diff --git a/testing/test_backup.py b/testing/test_backup.py new file mode 100644 index 00000000..bd06dba4 --- /dev/null +++ b/testing/test_backup.py @@ -0,0 +1,381 @@ +import pytest, time +from constants import simulator_fixed_words, simulator_fixed_xprv +from pycoin.key.BIP32Node import BIP32Node +from mnemonic import Mnemonic + + +@pytest.mark.qrcode +@pytest.mark.parametrize('multisig', [False, 'multisig']) +@pytest.mark.parametrize('st', ["b39pass", "eph", None]) +@pytest.mark.parametrize('reuse_pw', [False, True]) +@pytest.mark.parametrize('save_pw', [False, True]) +def test_make_backup(multisig, goto_home, pick_menu_item, cap_story, need_keypress, st, + open_microsd, microsd_path, unit_test, cap_menu, word_menu_entry, + pass_word_quiz, reset_seed_words, import_ms_wallet, get_setting, + cap_screen_qr, reuse_pw, save_pw, settings_set, settings_remove, + generate_ephemeral_words, set_bip39_pw, verify_backup_file, + check_and_decrypt_backup, restore_backup_cs, clear_ms): + # Make an encrypted 7z backup, verify it, and even restore it! + clear_ms() + reset_seed_words() + # need to make multisig in my main wallet + if multisig and st != "eph": + import_ms_wallet(15, 15) + need_keypress('y') + time.sleep(.1) + assert len(get_setting('multisig')) == 1 + + if st == "b39pass": + xfp_pass = set_bip39_pw("coinkite", reset=False) + _, story = cap_story() + assert "Above is the master key fingerprint of the current wallet" in story + need_keypress("y") + assert not get_setting('multisig', None) + elif st == "eph": + eph_seed = generate_ephemeral_words(num_words=24, dice=False, from_main=True) + _, story = cap_story() + assert "New ephemeral master key in effect" in story + need_keypress("y") + + if multisig: + # make multisig in ephemeral wallet + import_ms_wallet(15, 15, dev_key=True, common="605'/0'/0'") + need_keypress('y') + time.sleep(.1) + assert len(get_setting('multisig')) == 1 + + if reuse_pw: + settings_set('bkpw', ' '.join('zoo' for _ in range(12))) + else: + settings_remove('bkpw') + + goto_home() + pick_menu_item('Advanced/Tools') + pick_menu_item('Backup') + pick_menu_item('Backup System') + + title, body = cap_story() + if st: + if st == "b39pass": + assert "BIP39 passphrase is in effect" in body + assert "ignores passphrases and produces backup of main seed" in body + assert "(2) to back-up BIP39 passphrase wallet" in body + if st == "eph": + assert "An ephemeral seed is in effect" in body + assert "so backup will be of that seed" in body + + need_keypress("y") + time.sleep(.1) + title, body = cap_story() + + if reuse_pw: + assert ' 1: zoo' in body + assert '12: zoo' in body + need_keypress('y') + words = ['zoo']*12 + + time.sleep(0.1) + title, body = cap_story() + else: + assert title == 'NO-TITLE' + assert 'Record this' in body + assert 'password:' in body + + words = [w[3:].strip() for w in body.split('\n') if w and w[2] == ':'] + assert len(words) == 12 + + print("Passphrase: %s" % ' '.join(words)) + + if 'QR Code' in body: + need_keypress('1') + got_qr = cap_screen_qr().decode('ascii').lower().split() + assert [w[0:4] for w in words] == got_qr + need_keypress('y') + + # pass the quiz! + count, title, body = pass_word_quiz(words) + assert count >= 4 + assert "same words next time" in body + assert "Press (1) to save" in body + if save_pw: + need_keypress('1') + time.sleep(.1) + + assert get_setting('bkpw') == ' '.join(words) + else: + need_keypress('x') + time.sleep(.01) + assert get_setting('bkpw', 'xxx') == 'xxx' + + time.sleep(0.1) + title, body = cap_story() + + time.sleep(0.1) + if st == "b39pass" and multisig: + # correct settings switch back? + # multisig is only in main wallet + # must not be copied from main to b39pass + # must not be available after backup done + assert not get_setting('multisig', None) + + files = [] + for copy in range(2): + if copy == 1: + title, body = cap_story() + assert 'written:' in body + + fn = [ln.strip() for ln in body.split('\n') if ln.endswith('.7z')][0] + + print("filename %d: %s" % (copy, fn)) + + files.append(fn) + + # write extra copy. + need_keypress('2') + time.sleep(.01) + + bk_a = open_microsd(files[0]).read() + bk_b = open_microsd(files[1]).read() + + assert bk_a == bk_b, "contents mismatch" + + need_keypress('x') + time.sleep(.01) + + verify_backup_file(fn) + check_and_decrypt_backup(fn, words) + + for i in range(10): + need_keypress('x') + time.sleep(.01) + + # test verify on device (CRC check) + avail_settings = ['multisig'] if multisig else None + restore_backup_cs(files[0], words, avail_settings=avail_settings) + + +@pytest.mark.parametrize("stype", ["words12", "words24", "xprv"]) +def test_backup_ephemeral_wallet(stype, pick_menu_item, need_keypress, goto_home, + cap_story, pass_word_quiz, get_setting, + verify_backup_file, microsd_path, check_and_decrypt_backup, + sim_execfile, unit_test, word_menu_entry, cap_menu, + restore_backup_cs, generate_ephemeral_words, + import_ephemeral_xprv, reset_seed_words): + reset_seed_words() + goto_home() + if "words" in stype: + num_words = int(stype.replace("words", "")) + sec = generate_ephemeral_words(num_words, from_main=True) + else: + sec = import_ephemeral_xprv("sd", from_main=True) + + target = sim_execfile('devtest/get-secrets.py') + assert 'Error' not in target + need_keypress("y") + pick_menu_item("Advanced/Tools") + pick_menu_item("Backup") + pick_menu_item("Backup System") + time.sleep(.1) + title, story = cap_story() + assert "An ephemeral seed is in effect" in story + assert "so backup will be of that seed" in story + need_keypress("y") + time.sleep(.1) + title, story = cap_story() + if "Use same backup file password as last time?" in story: + need_keypress("x") + time.sleep(.1) + title, story = cap_story() + assert title == 'NO-TITLE' + assert 'Record this' in story + assert 'password:' in story + + words = [w[3:].strip() for w in story.split('\n') if w and w[2] == ':'] + assert len(words) == 12 + # pass the quiz! + count, title, body = pass_word_quiz(words) + assert count >= 4 + assert "same words next time" in body + assert "Press (1) to save" in body + need_keypress('x') + time.sleep(.01) + assert get_setting('bkpw', 'xxx') == 'xxx' + title, story = cap_story() + assert "Backup file written:" in story + fn = story.split("\n\n")[1] + assert fn.endswith(".7z") + verify_backup_file(fn) + contents = check_and_decrypt_backup(fn, words) + if "words" in stype: + assert "mnemonic" in contents + else: + assert "mnemonic" not in contents + assert simulator_fixed_words not in contents + assert simulator_fixed_xprv not in contents + assert target == contents + if "words" in stype: + words_str = " ".join(sec) + assert words_str in contents + seed = Mnemonic.to_seed(words_str) + expect = BIP32Node.from_master_secret(seed, netcode="XTN") + else: + expect = sec + + target_esk = None + target_epk = None + esk = expect.hwif(as_private=True) + epk = expect.hwif(as_private=False) + for line in contents.split("\n"): + if line.startswith("xprv ="): + target_esk = line.split("=")[-1].strip().replace('"', '') + if line.startswith("xpub ="): + target_epk = line.split("=")[-1].strip().replace('"', '') + assert target_epk == epk + assert target_esk == esk + + restore_backup_cs(fn, words) + + +@pytest.mark.parametrize("passphrase", ["@coinkite rulez!!", "!@#!@", "AAAAAAAAAAA"]) +def test_backup_bip39_wallet(passphrase, set_bip39_pw, pick_menu_item, need_keypress, + goto_home, cap_story, pass_word_quiz, get_setting, + verify_backup_file, microsd_path, check_and_decrypt_backup, + sim_execfile, unit_test, word_menu_entry, cap_menu, + restore_backup_cs): + goto_home() + set_bip39_pw(passphrase) + target = sim_execfile('devtest/get-secrets.py') + assert 'Error' not in target + need_keypress("y") + pick_menu_item("Advanced/Tools") + pick_menu_item("Backup") + pick_menu_item("Backup System") + time.sleep(.1) + title, story = cap_story() + assert "BIP39 passphrase is in effect" in story + assert "ignores passphrases and produces backup of main seed" in story + assert "(2) to back-up BIP39 passphrase wallet" in story + need_keypress("2") + time.sleep(.1) + title, story = cap_story() + if "Use same backup file password as last time?" in story: + need_keypress("x") + time.sleep(.1) + title, story = cap_story() + assert title == 'NO-TITLE' + assert 'Record this' in story + assert 'password:' in story + + words = [w[3:].strip() for w in story.split('\n') if w and w[2] == ':'] + assert len(words) == 12 + # pass the quiz! + count, title, body = pass_word_quiz(words) + assert count >= 4 + assert "same words next time" in body + assert "Press (1) to save" in body + need_keypress('x') + time.sleep(.01) + assert get_setting('bkpw', 'xxx') == 'xxx' + title, story = cap_story() + assert "Backup file written:" in story + fn = story.split("\n\n")[1] + assert fn.endswith(".7z") + verify_backup_file(fn) + contents = check_and_decrypt_backup(fn, words) + assert "mnemonic" not in contents + assert simulator_fixed_words not in contents + assert simulator_fixed_xprv not in contents + assert target == contents + seed = Mnemonic.to_seed(simulator_fixed_words, passphrase=passphrase) + expect = BIP32Node.from_master_secret(seed, netcode="XTN") + esk = expect.hwif(as_private=True) + epk = expect.hwif(as_private=False) + target_esk = None + target_epk = None + for line in contents.split("\n"): + if line.startswith("xprv ="): + target_esk = line.split("=")[-1].strip().replace('"', '') + if line.startswith("xpub ="): + target_epk = line.split("=")[-1].strip().replace('"', '') + assert target_epk == epk + assert target_esk == esk + + restore_backup_cs(fn, words) + + +def test_trick_backups(goto_trick_menu, clear_all_tricks, repl, unit_test, + new_trick_pin, new_pin_confirmed, pick_menu_item, need_keypress): + + from test_se2 import TC_REBOOT, TC_BLANK_WALLET + + clear_all_tricks() + + # - make wallets of all duress types (x2 each) + # - plus a few simple ones + # - perform a backup and check result + + for n in range(8): + goto_trick_menu() + pin = '123-%04d' % n + new_trick_pin(pin, 'Duress Wallet', None) + item = 'BIP-85 Wallet #%d' % (n % 4) if (n % 4 != 0) else 'Legacy Wallet' + pick_menu_item(item) + need_keypress('y') + new_pin_confirmed(pin, item, None, None) + + for pin, op_mode, expect, _, xflags in [ + ('11-33', 'Just Reboot', 'Reboot when this PIN', False, TC_REBOOT), + ('11-55', 'Look Blank', 'Look and act like a freshly', False, TC_BLANK_WALLET), + ]: + new_trick_pin(pin, op_mode, expect) + new_pin_confirmed(pin, op_mode, xflags) + + # works, but not the best test + # unit_test('devtest/backups.py') + + bk = repl.exec('import backups; RV.write(backups.render_backup_contents())', raw=1) + + assert 'Coldcard backup file' in bk + + def decode_backup(txt): + import json + vals = dict() + trimmed = dict() + for ln in txt.split('\n'): + if not ln: continue + if ln[0] == '#': continue + + k, v = ln.split(' = ', 1) + + v = json.loads(v) + + if k.startswith('duress_') or k.startswith('fw_'): + # no space in USB xfer for thesE! + trimmed[k] = v + else: + vals[k] = v + + return vals, trimmed + + # decode it + vals, trimmed = decode_backup(bk) + + assert 'duress_xprv' in trimmed + assert 'duress_1001_words' in trimmed + assert 'duress_1002_words' in trimmed + assert 'duress_1003_words' in trimmed + + unit_test('devtest/clear_seed.py') + + repl.exec(f'import backups; backups.restore_from_dict_ll({vals!r})') + + # recover from recovery + repl.exec(f'import backups; pa.setup(pa.pin); pa.login(); from actions import goto_top_menu; goto_top_menu()') + + bk2 = repl.exec('import backups; RV.write(backups.render_backup_contents())', raw=1) + assert 'Traceback' not in bk2 + + vals2, tr2 = decode_backup(bk2) + + assert vals == vals2 + assert trimmed == tr2 \ No newline at end of file diff --git a/testing/test_bip39pw.py b/testing/test_bip39pw.py index a2fdb90e..9a821624 100644 --- a/testing/test_bip39pw.py +++ b/testing/test_bip39pw.py @@ -9,7 +9,7 @@ from ckcc_protocol.protocol import CCProtocolPacker, CCProtoError, CCUserRefused from ckcc_protocol.constants import * import json from mnemonic import Mnemonic -from constants import simulator_fixed_xfp, simulator_fixed_words, simulator_fixed_xprv +from constants import simulator_fixed_xfp, simulator_fixed_words # add the BIP39 test vectors vectors = json.load(open('bip39-vectors.json'))['english'] @@ -137,7 +137,7 @@ def test_cancel_on_empty_added_numbers(pick_menu_item, goto_home, need_keypress, pick_menu_item('CANCEL') time.sleep(0.1) m = cap_menu() - assert m[0] == "Ready To Sign" + assert "Ready To Sign" in m[:3] @pytest.mark.parametrize('stype', ["bip39pw", "words", "xprv", None]) @@ -234,71 +234,4 @@ def test_bip39pass_on_ephemeral_seed(generate_ephemeral_words, import_ephemeral_ assert expect1.fingerprint().hex().upper() == xfp1 assert "press (2)" not in story - -@pytest.mark.parametrize("passphrase", ["@coinkite rulez!!", "!@#!@", "AAAAAAAAAAA"]) -def test_backup_bip39_wallet(passphrase, set_bip39_pw, pick_menu_item, need_keypress, - goto_home, cap_story, pass_word_quiz, get_setting, - verify_backup_file, microsd_path, check_and_decrypt_backup, - sim_execfile, unit_test, word_menu_entry, cap_menu, - restore_backup_cs): - goto_home() - set_bip39_pw(passphrase) - target = sim_execfile('devtest/get-secrets.py') - assert 'Error' not in target - need_keypress("y") - pick_menu_item("Advanced/Tools") - pick_menu_item("Backup") - pick_menu_item("Backup System") - time.sleep(.1) - title, story = cap_story() - assert "BIP39 passphrase is in effect" in story - assert "ignores passphrases and produces backup of main seed" in story - assert "(2) to back-up BIP39 passphrase wallet" in story - need_keypress("2") - time.sleep(.1) - title, story = cap_story() - if "Use same backup file password as last time?" in story: - need_keypress("x") - time.sleep(.1) - title, story = cap_story() - assert title == 'NO-TITLE' - assert 'Record this' in story - assert 'password:' in story - - words = [w[3:].strip() for w in story.split('\n') if w and w[2] == ':'] - assert len(words) == 12 - # pass the quiz! - count, title, body = pass_word_quiz(words) - assert count >= 4 - assert "same words next time" in body - assert "Press (1) to save" in body - need_keypress('x') - time.sleep(.01) - assert get_setting('bkpw', 'xxx') == 'xxx' - title, story = cap_story() - assert "Backup file written:" in story - fn = story.split("\n\n")[1] - assert fn.endswith(".7z") - verify_backup_file(fn) - contents = check_and_decrypt_backup(fn, words) - assert "mnemonic" not in contents - assert simulator_fixed_words not in contents - assert simulator_fixed_xprv not in contents - assert target == contents - seed = Mnemonic.to_seed(simulator_fixed_words, passphrase=passphrase) - expect = BIP32Node.from_master_secret(seed, netcode="XTN") - esk = expect.hwif(as_private=True) - epk = expect.hwif(as_private=False) - target_esk = None - target_epk = None - for line in contents.split("\n"): - if line.startswith("xprv ="): - target_esk = line.split("=")[-1].strip().replace('"', '') - if line.startswith("xpub ="): - target_epk = line.split("=")[-1].strip().replace('"', '') - assert target_epk == epk - assert target_esk == esk - - restore_backup_cs(fn, words) - # EOF diff --git a/testing/test_ephemeral.py b/testing/test_ephemeral.py index 5871c3be..691335ba 100644 --- a/testing/test_ephemeral.py +++ b/testing/test_ephemeral.py @@ -7,9 +7,6 @@ from constants import simulator_fixed_xpub from ckcc.protocol import CCProtocolPacker from txn import fake_txn from test_ux import word_menu_entry -from constants import simulator_fixed_words, simulator_fixed_xprv -from pycoin.key.BIP32Node import BIP32Node -from mnemonic import Mnemonic WORDLISTS = { @@ -119,10 +116,55 @@ def goto_eph_seed_menu(goto_home, pick_menu_item, cap_story, need_keypress): @pytest.fixture -def verify_ephemeral_secret_ui(cap_story, need_keypress, cap_menu, dev, fake_txn, try_sign, - goto_eph_seed_menu, reset_seed_words, get_identity_story, - get_seed_value_ux): - def doit(mnemonic=None, xpub=None, expected_xfp=None): +def restore_main_seed(goto_home, pick_menu_item, cap_story, cap_menu, + need_keypress, settings_path): + def list_settings_files(): + return [fn + for fn in os.listdir(settings_path("")) + if fn.endswith(".aes")] + + def doit(preserve_settings=False): + prev = len(list_settings_files()) + goto_home() + menu = cap_menu() + assert menu[-1] == "Restore Seed" + assert (menu[0][0] == "[") and (menu[0][-1] == "]") + pick_menu_item("Restore Seed") + time.sleep(.1) + title, story = cap_story() + + assert "Restore main wallet and its settings?\n\n" in story + assert "Press OK to forget current ephemeral wallet " in story + assert "settings, or press (1) to save & keep " in story + assert "those settings for later use." in story + + if preserve_settings: + ch = "1" + else: + ch = "y" + + need_keypress(ch) + time.sleep(.3) + + menu = cap_menu() + assert menu[-1] != "Restore Seed" + assert (menu[0][0] != "[") and (menu[0][-1] != "]") + + after = len(list_settings_files()) + if preserve_settings: + assert prev == after, "p%d == a%d" % (prev, after) + else: + assert prev > after, "p%d > a%d" % (prev, after) + + return doit + + +@pytest.fixture +def verify_ephemeral_secret_ui(cap_story, need_keypress, cap_menu, dev, fake_txn, + goto_eph_seed_menu, get_identity_story, try_sign, + get_seed_value_ux, pick_menu_item, goto_home, + restore_main_seed): + def doit(mnemonic=None, xpub=None, expected_xfp=None, preserve_settings=False): time.sleep(0.3) title, story = cap_story() in_effect_xfp = title[1:-1] @@ -132,7 +174,10 @@ def verify_ephemeral_secret_ui(cap_story, need_keypress, cap_menu, dev, fake_txn need_keypress("y") # just confirm new master key message menu = cap_menu() - assert menu[0] == "Ready To Sign" # returned to main menu + + assert expected_xfp in menu[0] if expected_xfp else True + assert menu[1] == "Ready To Sign" # returned to main menu + assert menu[-1] == "Restore Seed" # restore main from ephemeral ident_story = get_identity_story() assert "Ephemeral seed is in effect" in ident_story @@ -158,11 +203,13 @@ def verify_ephemeral_secret_ui(cap_story, need_keypress, cap_menu, dev, fake_txn # ephemeral seed chosen -> [xfp] will be visible assert menu[0] == f"[{ident_xfp}]" - reset_seed_words() + restore_main_seed(preserve_settings) goto_eph_seed_menu() menu = cap_menu() + assert menu[0] != f"[{ident_xfp}]" + return doit @@ -268,23 +315,29 @@ def import_ephemeral_xprv(microsd_path, virtdisk_path, goto_eph_seed_menu, @pytest.mark.parametrize("num_words", [12, 24]) @pytest.mark.parametrize("dice", [False, True]) +@pytest.mark.parametrize("preserve_settings", [False, True]) def test_ephemeral_seed_generate(num_words, generate_ephemeral_words, dice, reset_seed_words, goto_eph_seed_menu, - ephemeral_seed_disabled, verify_ephemeral_secret_ui): + ephemeral_seed_disabled, verify_ephemeral_secret_ui, + preserve_settings): reset_seed_words() + goto_eph_seed_menu() ephemeral_seed_disabled() e_seed_words = generate_ephemeral_words(num_words=num_words, dice=dice, from_main=True) - verify_ephemeral_secret_ui(mnemonic=e_seed_words) + verify_ephemeral_secret_ui(mnemonic=e_seed_words, + preserve_settings=preserve_settings) @pytest.mark.parametrize("num_words", [12, 18, 24]) @pytest.mark.parametrize("nfc", [False, True]) @pytest.mark.parametrize("truncated", [False, True]) +@pytest.mark.parametrize("preserve_settings", [False, True]) def test_ephemeral_seed_import_words(nfc, truncated, num_words, cap_menu, pick_menu_item, need_keypress, reset_seed_words, goto_eph_seed_menu, word_menu_entry, nfc_write_text, verify_ephemeral_secret_ui, - ephemeral_seed_disabled, get_seed_value_ux): + ephemeral_seed_disabled, get_seed_value_ux, + preserve_settings): if truncated and not nfc: return @@ -315,7 +368,8 @@ def test_ephemeral_seed_import_words(nfc, truncated, num_words, cap_menu, pick_m need_keypress("4") # understand consequences - verify_ephemeral_secret_ui(mnemonic=words.split(" "), expected_xfp=expect_xfp) + verify_ephemeral_secret_ui(mnemonic=words.split(" "), expected_xfp=expect_xfp, + preserve_settings=preserve_settings) nfc_seed = get_seed_value_ux(nfc=True) # export seed via NFC (always truncated) seed_words = get_seed_value_ux() @@ -323,12 +377,13 @@ def test_ephemeral_seed_import_words(nfc, truncated, num_words, cap_menu, pick_m @pytest.mark.parametrize("way", ["sd", "vdisk", "nfc"]) -@pytest.mark.parametrize('retry', range(3)) @pytest.mark.parametrize("testnet", [True, False]) -def test_ephemeral_seed_import_tapsigner(way, retry, testnet, pick_menu_item, cap_story, enter_hex, +@pytest.mark.parametrize("preserve_settings", [False, True]) +def test_ephemeral_seed_import_tapsigner(way, testnet, pick_menu_item, cap_story, enter_hex, need_keypress, reset_seed_words, goto_eph_seed_menu, verify_ephemeral_secret_ui, ephemeral_seed_disabled, - nfc_write_text, tapsigner_encrypted_backup): + nfc_write_text, tapsigner_encrypted_backup, + preserve_settings): reset_seed_words() fname, backup_key_hex, node = tapsigner_encrypted_backup(way, testnet=testnet) @@ -370,7 +425,7 @@ def test_ephemeral_seed_import_tapsigner(way, retry, testnet, pick_menu_item, ca assert "back of the card" in story need_keypress("y") # yes I have backup key enter_hex(backup_key_hex) - verify_ephemeral_secret_ui(xpub=node.hwif()) + verify_ephemeral_secret_ui(xpub=node.hwif(), preserve_settings=preserve_settings) @pytest.mark.parametrize("fail", ["wrong_key", "key_len", "plaintext", "garbage"]) @@ -466,15 +521,18 @@ def test_ephemeral_seed_import_tapsigner_real(data, pick_menu_item, cap_story, m @pytest.mark.parametrize("way", ["sd", "vdisk", "nfc"]) -@pytest.mark.parametrize('retry', range(3)) @pytest.mark.parametrize("testnet", [True, False]) -def test_ephemeral_seed_import_xprv(way, retry, testnet, reset_seed_words, +@pytest.mark.parametrize("preserve_settings", [False, True]) +def test_ephemeral_seed_import_xprv(way, testnet, reset_seed_words, goto_eph_seed_menu, verify_ephemeral_secret_ui, - ephemeral_seed_disabled, import_ephemeral_xprv): + ephemeral_seed_disabled, import_ephemeral_xprv, + preserve_settings): reset_seed_words() + goto_eph_seed_menu() ephemeral_seed_disabled() + node = import_ephemeral_xprv(way=way, testnet=testnet, from_main=True) - verify_ephemeral_secret_ui(xpub=node.hwif()) + verify_ephemeral_secret_ui(xpub=node.hwif(), preserve_settings=preserve_settings) def test_activate_current_tmp_secret(reset_seed_words, goto_eph_seed_menu, @@ -483,8 +541,8 @@ def test_activate_current_tmp_secret(reset_seed_words, goto_eph_seed_menu, word_menu_entry): reset_seed_words() goto_eph_seed_menu() - ephemeral_seed_disabled() + words, expected_xfp = WORDLISTS[12] pick_menu_item("Import Words") pick_menu_item(f"12 Words") @@ -510,86 +568,4 @@ def test_activate_current_tmp_secret(reset_seed_words, goto_eph_seed_menu, assert already_used_xfp == in_effect_xfp == expected_xfp need_keypress("y") - -@pytest.mark.parametrize("stype", ["words12", "words24", "xprv"]) -def test_backup_ephemeral_wallet(stype, pick_menu_item, need_keypress, goto_home, - cap_story, pass_word_quiz, get_setting, - verify_backup_file, microsd_path, check_and_decrypt_backup, - sim_execfile, unit_test, word_menu_entry, cap_menu, - restore_backup_cs, generate_ephemeral_words, - import_ephemeral_xprv, reset_seed_words): - reset_seed_words() - goto_home() - if "words" in stype: - num_words = int(stype.replace("words", "")) - sec = generate_ephemeral_words(num_words, from_main=True) - else: - sec = import_ephemeral_xprv("sd", from_main=True) - - target = sim_execfile('devtest/get-secrets.py') - assert 'Error' not in target - need_keypress("y") - pick_menu_item("Advanced/Tools") - pick_menu_item("Backup") - pick_menu_item("Backup System") - time.sleep(.1) - title, story = cap_story() - assert "An ephemeral seed is in effect" in story - assert "so backup will be of that seed" in story - need_keypress("y") - time.sleep(.1) - title, story = cap_story() - if "Use same backup file password as last time?" in story: - need_keypress("x") - time.sleep(.1) - title, story = cap_story() - assert title == 'NO-TITLE' - assert 'Record this' in story - assert 'password:' in story - - words = [w[3:].strip() for w in story.split('\n') if w and w[2] == ':'] - assert len(words) == 12 - # pass the quiz! - count, title, body = pass_word_quiz(words) - assert count >= 4 - assert "same words next time" in body - assert "Press (1) to save" in body - need_keypress('x') - time.sleep(.01) - assert get_setting('bkpw', 'xxx') == 'xxx' - title, story = cap_story() - assert "Backup file written:" in story - fn = story.split("\n\n")[1] - assert fn.endswith(".7z") - verify_backup_file(fn) - contents = check_and_decrypt_backup(fn, words) - if "words" in stype: - assert "mnemonic" in contents - else: - assert "mnemonic" not in contents - assert simulator_fixed_words not in contents - assert simulator_fixed_xprv not in contents - assert target == contents - if "words" in stype: - words_str = " ".join(sec) - assert words_str in contents - seed = Mnemonic.to_seed(words_str) - expect = BIP32Node.from_master_secret(seed, netcode="XTN") - else: - expect = sec - - target_esk = None - target_epk = None - esk = expect.hwif(as_private=True) - epk = expect.hwif(as_private=False) - for line in contents.split("\n"): - if line.startswith("xprv ="): - target_esk = line.split("=")[-1].strip().replace('"', '') - if line.startswith("xpub ="): - target_epk = line.split("=")[-1].strip().replace('"', '') - assert target_epk == epk - assert target_esk == esk - - restore_backup_cs(fn, words) - # EOF diff --git a/testing/test_se2.py b/testing/test_se2.py index 505ee101..03a84e52 100644 --- a/testing/test_se2.py +++ b/testing/test_se2.py @@ -733,82 +733,6 @@ def test_ux_changing_pins(true_pin, repl, force_main_pin, goto_trick_menu, clear_all_tricks() -def test_trick_backups(goto_trick_menu, clear_all_tricks, repl, unit_test, - new_trick_pin, new_pin_confirmed, pick_menu_item, need_keypress): - - clear_all_tricks() - - # - make wallets of all duress types (x2 each) - # - plus a few simple ones - # - perform a backup and check result - - for n in range(8): - goto_trick_menu() - pin = '123-%04d'%n - new_trick_pin(pin, 'Duress Wallet', None) - item = 'BIP-85 Wallet #%d' % (n%4) if (n%4 != 0) else 'Legacy Wallet' - pick_menu_item(item) - need_keypress('y') - new_pin_confirmed(pin, item, None, None) - - for pin, op_mode, expect, _, xflags in [ - ('11-33', 'Just Reboot', 'Reboot when this PIN', False, TC_REBOOT), - ('11-55', 'Look Blank', 'Look and act like a freshly', False, TC_BLANK_WALLET), - ]: - new_trick_pin(pin, op_mode, expect) - new_pin_confirmed(pin, op_mode, xflags) - - # works, but not the best test - #unit_test('devtest/backups.py') - - bk = repl.exec('import backups; RV.write(backups.render_backup_contents())', raw=1) - - assert 'Coldcard backup file' in bk - - def decode_backup(txt): - import json - vals = dict() - trimmed = dict() - for ln in txt.split('\n'): - if not ln: continue - if ln[0] == '#': continue - - k,v = ln.split(' = ', 1) - - v = json.loads(v) - - if k.startswith('duress_') or k.startswith('fw_'): - # no space in USB xfer for thesE! - trimmed[k] = v - else: - vals[k] = v - - return vals, trimmed - - # decode it - vals, trimmed = decode_backup(bk) - - assert 'duress_xprv' in trimmed - assert 'duress_1001_words' in trimmed - assert 'duress_1002_words' in trimmed - assert 'duress_1003_words' in trimmed - - unit_test('devtest/clear_seed.py') - - repl.exec(f'import backups; backups.restore_from_dict_ll({vals!r})') - - # recover from recovery - repl.exec(f'import backups; pa.setup(pa.pin); pa.login(); from actions import goto_top_menu; goto_top_menu()') - - bk2 = repl.exec('import backups; RV.write(backups.render_backup_contents())', raw=1) - assert 'Traceback' not in bk2 - - vals2, tr2 = decode_backup(bk2) - - assert vals == vals2 - assert trimmed == tr2 - - # TODO # - make trick and do login, check arrives right state? # - out of slots diff --git a/testing/test_sign.py b/testing/test_sign.py index 42c3d880..1b5f5a96 100644 --- a/testing/test_sign.py +++ b/testing/test_sign.py @@ -359,6 +359,7 @@ def test_vs_bitcoind(match_key, use_regtest, check_against_bitcoind, bitcoind, s if hex(got_xfp) != hex(wallet_xfp): raise pytest.xfail("wrong HD master key fingerprint") + start_sign(psbt, finalize=we_finalize) if mine.txn: # pull out included txn txn2 = B2A(mine.txn) @@ -367,8 +368,6 @@ def test_vs_bitcoind(match_key, use_regtest, check_against_bitcoind, bitcoind, s else: assert mine.version == 2 - start_sign(psbt, finalize=we_finalize) - signed = end_sign(accept=True, finalize=we_finalize) open('debug/vs-signed.psbt', 'wb').write(signed) @@ -964,6 +963,7 @@ def test_finalization_vs_bitcoind(match_key, use_regtest, check_against_bitcoind if hex(got_xfp) != hex(wallet_xfp): raise pytest.xfail("wrong HD master key fingerprint") + start_sign(psbt, finalize=True) if mine.txn: # pull out included txn (only available in PSBTv0) txn2 = B2A(mine.txn) @@ -972,8 +972,6 @@ def test_finalization_vs_bitcoind(match_key, use_regtest, check_against_bitcoind else: assert mine.version == 2 - start_sign(psbt, finalize=True) - signed_final = end_sign(accept=True, finalize=True) assert signed_final[0:4] != b'psbt', "expecting raw bitcoin txn" open('debug/finalized-by-ckcc.txn', 'wt').write(B2A(signed_final)) diff --git a/testing/test_ux.py b/testing/test_ux.py index b6383edc..34548464 100644 --- a/testing/test_ux.py +++ b/testing/test_ux.py @@ -117,157 +117,6 @@ def pass_word_quiz(need_keypress, cap_story): return doit -@pytest.mark.qrcode -@pytest.mark.parametrize('multisig', [False, 'multisig']) -@pytest.mark.parametrize('st', ["b39pass", "eph", None]) -@pytest.mark.parametrize('reuse_pw', [False, True]) -@pytest.mark.parametrize('save_pw', [False, True]) -def test_make_backup(multisig, goto_home, pick_menu_item, cap_story, need_keypress, st, - open_microsd, microsd_path, unit_test, cap_menu, word_menu_entry, - pass_word_quiz, reset_seed_words, import_ms_wallet, get_setting, - cap_screen_qr, reuse_pw, save_pw, settings_set, settings_remove, - generate_ephemeral_words, set_bip39_pw, verify_backup_file, - check_and_decrypt_backup, restore_backup_cs): - # Make an encrypted 7z backup, verify it, and even restore it! - - # need to make multisig in my main wallet - if multisig and st != "eph": - import_ms_wallet(15, 15) - need_keypress('y') - time.sleep(.1) - assert len(get_setting('multisig')) == 1 - - if st == "b39pass": - xfp_pass = set_bip39_pw("coinkite", reset=False) - _, story = cap_story() - assert "Above is the master key fingerprint of the current wallet" in story - need_keypress("y") - assert not get_setting('multisig', None) - elif st == "eph": - eph_seed = generate_ephemeral_words(num_words=24, dice=False, from_main=True) - _, story = cap_story() - assert "New ephemeral master key in effect" in story - need_keypress("y") - - if multisig: - # make multisig in ephemeral wallet - import_ms_wallet(15, 15, dev_key=True, common="605'/0'/0'") - need_keypress('y') - time.sleep(.1) - assert len(get_setting('multisig')) == 1 - - if reuse_pw: - settings_set('bkpw', ' '.join('zoo' for _ in range(12))) - else: - settings_remove('bkpw') - - goto_home() - pick_menu_item('Advanced/Tools') - pick_menu_item('Backup') - pick_menu_item('Backup System') - - title, body = cap_story() - if st: - if st == "b39pass": - assert "BIP39 passphrase is in effect" in body - assert "ignores passphrases and produces backup of main seed" in body - assert "(2) to back-up BIP39 passphrase wallet" in body - if st == "eph": - assert "An ephemeral seed is in effect" in body - assert "so backup will be of that seed" in body - - need_keypress("y") - time.sleep(.1) - title, body = cap_story() - - if reuse_pw: - assert ' 1: zoo' in body - assert '12: zoo' in body - need_keypress('y') - words = ['zoo']*12 - - time.sleep(0.1) - title, body = cap_story() - else: - assert title == 'NO-TITLE' - assert 'Record this' in body - assert 'password:' in body - - words = [w[3:].strip() for w in body.split('\n') if w and w[2] == ':'] - assert len(words) == 12 - - print("Passphrase: %s" % ' '.join(words)) - - if 'QR Code' in body: - need_keypress('1') - got_qr = cap_screen_qr().decode('ascii').lower().split() - assert [w[0:4] for w in words] == got_qr - need_keypress('y') - - # pass the quiz! - count, title, body = pass_word_quiz(words) - assert count >= 4 - assert "same words next time" in body - assert "Press (1) to save" in body - if save_pw: - need_keypress('1') - time.sleep(.1) - - assert get_setting('bkpw') == ' '.join(words) - else: - need_keypress('x') - time.sleep(.01) - assert get_setting('bkpw', 'xxx') == 'xxx' - - time.sleep(0.1) - title, body = cap_story() - - time.sleep(0.1) - if st == "b39pass" and multisig: - # correct settings switch back? - # multisig is only in main wallet - # must not be copied from main to b39pass - # must not be available after backup done - assert not get_setting('multisig', None) - - files = [] - for copy in range(2): - if copy == 1: - title, body = cap_story() - assert 'written:' in body - - fn = [ln.strip() for ln in body.split('\n') if ln.endswith('.7z')][0] - - print("filename %d: %s" % (copy, fn)) - - files.append(fn) - - # write extra copy. - need_keypress('2') - time.sleep(.01) - - bk_a = open_microsd(files[0]).read() - bk_b = open_microsd(files[1]).read() - - assert bk_a == bk_b, "contents mismatch" - - need_keypress('x') - time.sleep(.01) - - verify_backup_file(fn) - check_and_decrypt_backup(fn, words) - - for i in range(10): - need_keypress('x') - time.sleep(.01) - - # test verify on device (CRC check) - avail_settings = ['multisig'] if multisig else None - restore_backup_cs(files[0], words, avail_settings=avail_settings) - - reset_seed_words() - settings_remove('multisig') - @pytest.mark.qrcode @pytest.mark.parametrize('seed_words, xfp', [ diff --git a/unix/sim_boot.py b/unix/sim_boot.py index 9c4f7654..2fb65d1c 100644 --- a/unix/sim_boot.py +++ b/unix/sim_boot.py @@ -29,12 +29,11 @@ if '--sflash' not in sys.argv: #glob.settings.current = dict(sim_defaults) -if 1: - # Install Mk4 hacks and workarounds - import mk4 - import sim_mk4 - import sim_psram - import sim_vdisk +# Install Mk4 hacks and workarounds +import mk4 +import sim_mk4 +import sim_psram +import sim_vdisk if sys.argv[-1] != '-q': import main # must be last, does not return diff --git a/unix/variant/sim_se2.py b/unix/variant/sim_se2.py index 331e0a35..27df35fb 100644 --- a/unix/variant/sim_se2.py +++ b/unix/variant/sim_se2.py @@ -3,6 +3,7 @@ # - do not import this file before trick_pins has had a chance to be imported # from binascii import a2b_base64, b2a_base64 +from binascii import unhexlify as a2b_hex from errno import ENOENT # these flags are masked-out from mpy so even it can't tell they happened @@ -53,11 +54,16 @@ class SecondSecureElement: from trick_pins import TRICK_SLOT_LAYOUT import uctypes from nvstore import SettingsObject - + from sim_secel import SECRETS self.state = {} obj = SettingsObject() + obj.set_key(a2b_hex(SECRETS["_pin1_secret"])) + obj.load() + # merging default values as they contain useful nfc,vidsk info + obj.merge_previous_active(obj.default_values()) + obj.save() s = obj.get('_se2', None) if not s: print("no SE2 data")