diff --git a/releases/ChangeLog.md b/releases/ChangeLog.md index 802e57df..7ce93ac8 100644 --- a/releases/ChangeLog.md +++ b/releases/ChangeLog.md @@ -1,3 +1,11 @@ + +- Enhancement: adds BIP-85 support: "One seed to rule them all". Takes the + secret of this Coldcard, and (safely) constructs a secret/seed phrase you + can import into another wallet system. Supports BIP-39 seeds of 12,18 or 24 words, + "HDSeed" (WIF private key) for Bitcoin Core, a fresh XPRV for BIP-32 systems, + or even 32/64 bytes of hex for other applications. The point of this is your + Coldcard backup also backs up the new wallet, since it's root secret is + deterministically derived. Advanced > DangerZone > Derive Entropy. - Enhancement: QR Code rendering improved. Should be more readable in more cases. Faster. ## 3.1.3 - April 30, 2020 diff --git a/shared/actions.py b/shared/actions.py index 3cc01932..693d7db6 100644 --- a/shared/actions.py +++ b/shared/actions.py @@ -562,16 +562,26 @@ async def view_seed_words(*a): return with stash.SensitiveValues() as sv: - assert sv.mode == 'words' # protected by menu item predicate + if sv.mode == 'words': + words = tcc.bip39.from_data(sv.raw).split(' ') - words = tcc.bip39.from_data(sv.raw).split(' ') + msg = 'Seed words (%d):\n' % len(words) + msg += '\n'.join('%2d: %s' % (i+1, w) for i,w in enumerate(words)) - msg = 'Seed words (%d):\n' % len(words) - msg += '\n'.join('%2d: %s' % (i+1, w) for i,w in enumerate(words)) + pw = stash.bip39_passphrase + if pw: + msg += '\n\nBIP39 Passphrase:\n%s' % stash.bip39_passphrase + elif sv.mode == 'xprv': + import chains + msg = chains.current_chain().serialize_private(sv.node) - pw = stash.bip39_passphrase - if pw: - msg += '\n\nBIP39 Passphrase:\n%s' % stash.bip39_passphrase + elif sv.mode == 'master': + from ubinascii import hexlify as b2a_hex + + msg = '%d bytes:\n\n' % len(sv.raw) + msg += str(b2a_hex(sv.raw), 'ascii') + else: + raise ValueError(sv.mode) await ux_show_story(msg, sensitive=True) diff --git a/shared/drv_entro.py b/shared/drv_entro.py new file mode 100644 index 00000000..a5d2c2b9 --- /dev/null +++ b/shared/drv_entro.py @@ -0,0 +1,196 @@ +# (c) Copyright 2020 by Coinkite Inc. This file is part of Coldcard +# and is covered by GPLv3 license found in COPYING. +# +# BIP-85: Deterministic Entropy From BIP32 Keychains, by +# Ethan Kosakovsky +# +# Using the system's BIP32 master key, safely derive seeds phrases/entropy for other +# wallet systems, which may expect seed phrases, XPRV, or other entropy. +# +import stash, tcc, hmac, chains +from ux import ux_show_story, ux_enter_number, the_ux, ux_confirm +from menu import MenuItem, MenuSystem +from ubinascii import hexlify as b2a_hex +from serializations import hash160 + +def drv_entro_start(*a): + + # UX entry + ch = await ux_show_story('''\ +Create Entropy for Other Wallets (BIP-85) + +This feature derives "entropy" based mathematically on this wallet's seed value. \ +This will be displayed as a 12 or 24 word seed phrase, \ +or formatted in other ways to make it easy to import into \ +other wallet systems. + +You can recreate this value later, based \ +only the seed-phrase or backup of this Coldcard. + +There is no way to reverse the process, should the other wallet system be compromised, \ +so the other wallet is effectively segregated from the Coldcard and yet \ +still backed-up.''') + if ch != 'y': return + + if stash.bip39_passphrase: + if not await ux_confirm('''You have a BIP39 passphrase set right now and so that will become wrapped into the new secret.'''): + return + + choices = [ '12 words', '18 words', '24 words', 'WIF (privkey)', + 'XPRV (BIP32)', '32-bytes hex', '64-bytes hex'] + + m = MenuSystem([MenuItem(c, f=drv_entro_step2) for c in choices]) + the_ux.push(m) + +def drv_entro_step2(_1, picked, _2): + from main import dis + from files import CardSlot, CardMissingError + + the_ux.pop() + + index = await ux_enter_number("Index Number?", 9999) + + if picked in (0,1,2): + # BIP39 seed phrases (we only support English) + num_words = (12, 18, 24)[picked] + width = (16, 24, 32)[picked] # of bytes + path = "m/83696968'/39'/0'/{num_words}'/{index}'".format(num_words=num_words, index=index) + s_mode = 'words' + elif picked == 3: + # HDSeed for Bitcoin Core: but really a WIF of a private key, can be used anywhere + s_mode = 'wif' + path = "m/83696968'/2'/{index}'".format(index=index) + width = 32 + elif picked == 4: + # New XPRV + path = "m/83696968'/32'/{index}'".format(index=index) + s_mode = 'xprv' + width = 64 + elif picked in (5, 6): + width = 32 if picked == 5 else 64 + path = "m/83696968'/128169'/{width}'/{index}'".format(width=width, index=index) + s_mode = 'hex' + else: + raise ValueError(picked) + + dis.fullscreen("Working...") + encoded = None + + with stash.SensitiveValues() as sv: + node = sv.derive_path(path) + entropy = hmac.HMAC(b'bip-entropy-from-k', node.private_key(), tcc.sha512).digest() + sv.register(entropy) + + # truncate for this application + new_secret = entropy[0:width] + + + # only "new_secret" is interesting past here (node already blanked at this point) + del node + + # Reveal to user! + chain = chains.current_chain() + + if s_mode == 'words': + # BIP39 seed phrase, various lengths + words = tcc.bip39.from_data(new_secret).split(' ') + + msg = 'Seed words (%d):\n' % len(words) + msg += '\n'.join('%2d: %s' % (i+1, w) for i,w in enumerate(words)) + + encoded = stash.SecretStash.encode(seed_phrase=new_secret) + + elif s_mode == 'wif': + # for Bitcoin Core: a 32-byte of secret exponent, base58 w/ prefix 0x80 + # - always "compressed", so has suffix of 0x01 (inside base58) + # - we're not checking it's on curve + # - we have no way to represent this internally, since we rely on bip32 + + # append 0x01 to indicate it's a compressed private key + pk = new_secret + b'\x01' + + msg = 'WIF (privkey):\n' + tcc.codecs.b58_encode(chain.b58_privkey + pk) + + elif s_mode == 'xprv': + # Raw XPRV value. + ch, pk = new_secret[0:32], new_secret[32:64] + master_node = tcc.bip32.HDNode(chain_code=ch, private_key=pk, + child_num=0, depth=0, fingerprint=0) + + encoded = stash.SecretStash.encode(xprv=master_node) + + msg = 'Derived XPRV:\n' + chain.serialize_private(master_node) + + elif s_mode == 'hex': + # Random hex number for whatever purpose + msg = ('Hex (%d bytes):\n' % width) + str(b2a_hex(new_secret), 'ascii') + + stash.blank_object(new_secret) + new_secret = None # no need to print it again + else: + raise ValueError(s_mode) + + msg += '\n\nPath Used (index=%d):\n %s' % (index, path) + + if new_secret: + msg += '\n\nRaw Entropy:\n' + str(b2a_hex(new_secret), 'ascii') + + print(msg) # XXX debug + + prompt = '\n\nPress 1 to save to MicroSD card' + if encoded is not None: + prompt += ', 2 to switch to derived secret.' + + while 1: + ch = await ux_show_story(msg+prompt, sensitive=True, escape='12') + + if ch == '1': + # write to SD card: simple text file + try: + with CardSlot() as card: + fname, out_fn = card.pick_filename('drv-%s-idx%d.txt' % (s_mode, index)) + + with open(fname, 'wt') as fp: + fp.write(msg) + fp.write('\n') + except CardMissingError: + await needs_microsd() + continue + except Exception as e: + await ux_show_story('Failed to write!\n\n\n'+str(e)) + continue + + await ux_show_story("Filename is:\n\n%s" % out_fn, title='Saved') + else: + break + + if new_secret is not None: + stash.blank_object(new_secret) + stash.blank_object(msg) + + if ch == '2' and (encoded is not None): + from main import pa, settings, dis + from pincodes import AE_SECRET_LEN + + # switch over to new secret! + dis.fullscreen("Applying...") + + stash.bip39_passphrase = '' + tmp_secret = encoded + bytes(AE_SECRET_LEN - len(encoded)) + + # monkey-patch to block SE access, and just use new secret + pa.fetch = lambda *a, **k: bytearray(tmp_secret) + pa.change = lambda *a, **k: None + pa.ls_fetch = pa.change + pa.ls_change = pa.change + + # copies system settings to new encrypted-key value, calculates + # XFP, XPUB and saves into that, and starts using them. + pa.new_main_secret(pa.fetch()) + + await ux_show_story("New master key in effect until next power down.") + + if encoded is not None: + stash.blank_object(encoded) + +# EOF diff --git a/shared/flow.py b/shared/flow.py index 7ebcd21a..2558a18c 100644 --- a/shared/flow.py +++ b/shared/flow.py @@ -16,8 +16,10 @@ if version.has_fatram: from hsm import hsm_policy_available + from drv_entro import drv_entro_start else: hsm_policy_available = lambda: False + drv_entro_start = None # # NOTE: "Always In Title Case" @@ -131,10 +133,8 @@ def has_secrets(): DangerZoneMenu = [ # xxxxxxxxxxxxxxxx MenuItem("Debug Functions", menu=DebugFunctionsMenu), # actually harmless - MenuItem('Lock Down Seed', f=convert_bip39_to_bip32, - predicate=lambda: settings.get('words', True)), - MenuItem('View Seed Words', f=view_seed_words, - predicate=lambda: settings.get('words', True)), + MenuItem('Lock Down Seed', f=convert_bip39_to_bip32), + MenuItem('View Seed Words', f=view_seed_words), # text is a little wrong sometimes, rare MenuItem("Destroy Seed", f=clear_seed), MenuItem("I Am Developer.", menu=maybe_dev_menu), MenuItem("Wipe Patch Area", f=wipe_filesystem), # needs better label @@ -160,6 +160,7 @@ def has_secrets(): MenuItem('Paper Wallets', f=make_paper_wallet), MenuItem("Address Explorer", f=address_explore), MenuItem('User Management', menu=make_users_menu, predicate=lambda: version.has_fatram), + MenuItem('Derive Entropy', f=drv_entro_start, predicate=lambda: version.has_fatram), MenuItem("Danger Zone", menu=DangerZoneMenu), ] diff --git a/shared/seed.py b/shared/seed.py index cc1af59b..27873181 100644 --- a/shared/seed.py +++ b/shared/seed.py @@ -468,11 +468,6 @@ async def remember_bip39_passphrase(): dis.fullscreen('Check...') with stash.SensitiveValues() as sv: - if sv.mode != 'words': - # not a BIP39 derived secret, so cannot work. - await ux_show_story('''The wallet secret was not based on a seed phrase, so we cannot add a BIP39 passphrase at this time.''', title='Failed') - return - nv = SecretStash.encode(xprv=sv.node) # Important: won't write new XFP to nvram if pw still set diff --git a/shared/stash.py b/shared/stash.py index 7caa5f48..99faf8dc 100644 --- a/shared/stash.py +++ b/shared/stash.py @@ -124,10 +124,12 @@ def __init__(self, secret=None, bypass_pw=False): raise ValueError('no secrets yet') self.secret = pa.fetch() + self.spots = [ self.secret ] else: # sometimes we already know it - assert set(secret) != {0} + #assert set(secret) != {0} self.secret = secret + self.spots = [] # backup during volatile bip39 encryption: do not use passphrase self._bip39pw = '' if bypass_pw else str(bip39_passphrase) @@ -137,9 +139,10 @@ def __enter__(self): self.mode, self.raw, self.node = SecretStash.decode(self.secret, self._bip39pw) - self.chain = chains.current_chain() + self.spots.append(self.node) + self.spots.append(self.raw) - self.spots = [ self.secret, self.node, self.raw ] + self.chain = chains.current_chain() return self @@ -151,7 +154,6 @@ def __exit__(self, exc_type, exc_val, exc_tb): if hasattr(self, 'secret'): # will be blanked from above - assert self.secret == bytes(AE_SECRET_LEN) del self.secret if hasattr(self, 'node'): diff --git a/testing/conftest.py b/testing/conftest.py index db7db4cf..7e84ade0 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -471,6 +471,32 @@ def doit(prv): # - actually need seed words for all tests reset_seed_words() +@pytest.fixture(scope="function") +def set_encoded_secret(sim_exec, sim_execfile, simulator, reset_seed_words): + # load simulator w/ a specific secret + + def doit(encoded): + assert 17 <= len(encoded) <= 72 + + encoded += bytes(72- len(encoded)) + + sim_exec('import main; main.ENCODED_SECRET = %r; ' % encoded) + rv = sim_execfile('devtest/set_encoded_secret.py') + if rv: pytest.fail(rv) + + simulator.start_encryption() + simulator.check_mitm() + + #print("sim xfp: 0x%08x" % simulator.master_fingerprint) + + return simulator.master_fingerprint + + yield doit + + # Important cleanup: restore normal key, because other tests assume that + # - actually need seed words for all tests + reset_seed_words() + @pytest.fixture(scope="function") def set_seed_words(sim_exec, sim_execfile, simulator, reset_seed_words): # load simulator w/ a specific bip32 master key @@ -515,7 +541,6 @@ def doit(): - @pytest.fixture() def settings_set(sim_exec): diff --git a/testing/devtest/set_encoded_secret.py b/testing/devtest/set_encoded_secret.py new file mode 100644 index 00000000..4d09b668 --- /dev/null +++ b/testing/devtest/set_encoded_secret.py @@ -0,0 +1,19 @@ +# load up the simulator w/ indicated encoded secret. could be xprv/words/etc. +import tcc, main +from sim_settings import sim_defaults +import stash, chains +from h import b2a_hex +from main import settings, pa +from stash import SecretStash, SensitiveValues +from utils import xfp2str + +settings.current = sim_defaults +settings.overrides.clear() + +raw = main.ENCODED_SECRET +pa.change(new_secret=raw) +pa.new_main_secret(raw) + +print("New key in effect: %s" % settings.get('xpub', 'MISSING')) +print("Fingerprint: %s" % xfp2str(settings.get('xfp', 0))) + diff --git a/testing/test_drv_entro.py b/testing/test_drv_entro.py new file mode 100644 index 00000000..4aa2e2a9 --- /dev/null +++ b/testing/test_drv_entro.py @@ -0,0 +1,243 @@ +# (c) Copyright 2018 by Coinkite Inc. This file is part of Coldcard +# and is covered by GPLv3 license found in COPYING. +# +# test drv_entro.py features +# +import pytest, time, os, re +from binascii import a2b_hex, b2a_hex +from helpers import B2A +from pycoin.key.BIP32Node import BIP32Node +from pycoin.key.Key import Key +from mnemonic import Mnemonic + +# XPRV from spec: xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb +EXAMPLE_XPRV = '011b67969d1ec69bdfeeae43213da8460ba34b92d0788c8f7bfcfa44906e8a589c3f15e5d852dc2e9ba5e9fe189a8dd2e1547badef5b563bbe6579fc6807d80ed900000000000000' + + +@pytest.mark.parametrize('mode,index,entropy,expect', [ + ('12 words', 0, + '6250b68daf746d12a24d58b4787a714b', + 'girl mad pet galaxy egg matter matrix prison refuse sense ordinary nose'), + ('18 words', 0, + '938033ed8b12698449d4bbca3c853c66b293ea1b1ce9d9dc', + 'near account window bike charge season chef number sketch tomorrow excuse sniff circle vital hockey outdoor supply token'), + ('24 words', 0, + 'ae131e2312cdc61331542efe0d1077bac5ea803adf24b313a4f0e48e9c51f37f', + 'puppy ocean match cereal symbol another shed magic wrap hammer bulb intact gadget divorce twin tonight reason outdoor destroy simple truth cigar social volcano'), + + ('WIF (privkey)', 0, + '7040bb53104f27367f317558e78a994ada7296c6fde36a364e5baf206e502bb1', + 'Kzyv4uF39d4Jrw2W7UryTHwZr1zQVNk4dAFyqE6BuMrMh1Za7uhp'), + ('XPRV (BIP32)', 0, + None, + 'xprv9s21ZrQH143K2srSbCSg4m4kLvPMzcWydgmKEnMmoZUurYuBuYG46c6P71UGXMzmriLzCCBvKQWBUv3vPB3m1SATMhp3uEjXHJ42jFg7myX'), + ('32-bytes hex', 0, + None, + 'ea3ceb0b02ee8e587779c63f4b7b3a21e950a213f1ec53cab608d13e8796e6dc'), + ('64-bytes hex', 0, + None, + '492db4698cf3b73a5a24998aa3e9d7fa96275d85724a91e71aa2d645442f878555d078fd1f1f67e368976f04137b1f7a0d19232136ca50c44614af72b5582a5c'), +]) +def test_bip_vectors(mode, index, entropy, expect, + set_encoded_secret, dev, cap_menu, pick_menu_item, + goto_home, cap_story, need_keypress, microsd_path, settings_set, sim_eval, sim_exec +): + + set_encoded_secret(a2b_hex(EXAMPLE_XPRV)) + settings_set('chain', 'BTC') + + goto_home() + pick_menu_item('Advanced') + pick_menu_item('Derive Entropy') + + time.sleep(0.1) + title, story = cap_story() + + assert 'seed value' in story + assert 'other wallet systems' in story + + need_keypress('y') + time.sleep(0.1) + + pick_menu_item(mode) + + if index is not None: + time.sleep(0.1) + for n in str(index): + need_keypress(n) + + need_keypress('y') + + time.sleep(0.1) + title, story = cap_story() + + assert f'Path Used (index={index}):' in story + assert "m/83696968'/" in story + assert f"/{index}'" in story + + if entropy is not None: + assert f"Raw Entropy:\n{entropy}" in story + + do_import = False + + if 'words' in mode: + num_words = int(mode.split()[0]) + assert f'Seed words ({num_words}):' in story + assert f"m/83696968'/39'/0'/{num_words}'/{index}'" in story + assert '\n 1: ' in story + assert f'\n{num_words}: ' in story + got = [ln[4:] for ln in story.split('\n') if len(ln)>5 and ln[2] == ':'] + assert ' '.join(got) == expect + do_import = 'words' + + elif 'XPRV' in mode: + assert 'Derived XPRV:' in story + assert f"m/83696968'/32'/{index}'" in story + assert expect in story + do_import = 'xprv' + + elif 'WIF' in mode: + assert 'WIF (privkey)' in story + assert f"m/83696968'/2'/{index}'" in story + assert expect in story + + elif 'bytes hex' in mode: + width = int(mode.split('-')[0]) + assert width in { 32, 64} + assert f'Hex ({width} bytes):' in story + assert f"m/83696968'/128169'/{width}'/{index}'" in story + assert expect in story + + else: + raise ValueError(mode) + + # write to SD + msg = story.split('Press', 1)[0] + if 1: + assert 'Press 1 to save' in story + need_keypress('1') + + time.sleep(0.1) + title, story = cap_story() + + assert title == 'Saved' + fname = story.split('\n')[-1] + need_keypress('y') + + time.sleep(0.1) + title, story = cap_story() + + assert story.startswith(msg) + + path = microsd_path(fname) + assert path.endswith('.txt') + txt = open(path, 'rt').read() + + assert txt.strip() == msg.strip() + + + if do_import: + assert '2 to switch to derived secret' in story + + try: + time.sleep(0.1) + need_keypress('2') + + time.sleep(0.1) + title, story = cap_story() + assert 'New master key in effect' in story + + encoded = sim_eval('main.pa.fetch()') + print(encoded) + assert encoded.startswith('bytearray(b') + encoded = eval(encoded) + assert len(encoded) == 72 + + marker = encoded[0] + if do_import == 'words': + assert marker & 0x80 == 0x80 + width = ((marker & 0x3) + 2) * 8 + assert width in {16, 24, 32} + assert encoded[1:1+width] == a2b_hex(entropy) + elif do_import == 'xprv': + assert marker == 0x01 + node = BIP32Node.from_hwif(expect) + ch, pk = encoded[1:33], encoded[33:65] + assert node.chain_code() == ch + assert node.secret_exponent() == int(B2A(pk), 16) + finally: + # required cleanup + sim_exec('import main; from pincodes import PinAttempt; ' + 'main.pa = PinAttempt(); main.pa.setup("12-12"); main.pa.login();') + + + need_keypress('x') + +HISTORY = set() + +@pytest.mark.parametrize('mode,pattern', [ + ('WIF (privkey)', r'[1-9A-HJ-NP-Za-km-z]{51,52}' ), + ('XPRV (BIP32)', r'[tx]prv[1-9A-HJ-NP-Za-km-z]{107}'), + ('32-bytes hex', r'[a-f0-9]{32}'), + ('64-bytes hex', r'[a-f0-9]{64}'), + ('12 words', r'[a-f0-9]{32}'), + ('18 words', r'[a-f0-9]{48}'), + ('24 words', r'[a-f0-9]{64}'), +]) +@pytest.mark.parametrize('index', [0, 1, 10, 100, 1000, 9999]) +def test_path_index(mode, pattern, index, + set_encoded_secret, dev, cap_menu, pick_menu_item, + goto_home, cap_story, need_keypress +): + # Uses any key on Simulator; just checking for operation + entropy level + + goto_home() + pick_menu_item('Advanced') + pick_menu_item('Derive Entropy') + + time.sleep(0.1) + title, story = cap_story() + + assert 'seed value' in story + assert 'other wallet systems' in story + + need_keypress('y') + time.sleep(0.1) + + pick_menu_item(mode) + + if index is not None: + time.sleep(0.1) + for n in str(index): + need_keypress(n) + + need_keypress('y') + + time.sleep(0.1) + title, story = cap_story() + + assert f'Path Used (index={index}):' in story + assert "m/83696968'/" in story + assert f"/{index}'" in story + + got = re.findall(pattern, story)[0] + + assert len(set(got)) >= 12 + + global HISTORY + assert got not in HISTORY + HISTORY.add(got) + + if 'words' in mode: + exp = Mnemonic('english').to_mnemonic(a2b_hex(got)).split() + assert '\n'.join(f'{n+1:2d}: {w}' for n, w in enumerate(exp)) in story + elif 'XPRV' in mode: + node = BIP32Node.from_hwif(got) + assert str(b2a_hex(node.chain_code()), 'ascii') in story + assert hex(node.secret_exponent())[2:] in story + elif 'WIF' in mode: + key = Key.from_text(got) + assert hex(key.secret_exponent())[2:] in story + + +# EOF diff --git a/unix/README.md b/unix/README.md index f7c3ac3e..989ef91c 100644 --- a/unix/README.md +++ b/unix/README.md @@ -38,6 +38,7 @@ wallet (on testnet, always with the same seed). But there are other options: - `--users` => preset a few users: "totp", "hotp" and "pw" - `--user-mgmt` => go to the User Management menu inside settings - `--pin 123456-123456` => set PIN code to indicated value +- `--deriv` => go to the Derive Entropy menu inside settings, also loads XPRV from BIP - `--secret 01abababab...` => directly set contents of SE secret, see SecretStash.encode() See `frozen-modules/sim-settings.py` for the details of settings-related options. diff --git a/unix/frozen-modules/sim_quickstart.py b/unix/frozen-modules/sim_quickstart.py index b8ba6a4b..5cd9ab08 100644 --- a/unix/frozen-modules/sim_quickstart.py +++ b/unix/frozen-modules/sim_quickstart.py @@ -92,13 +92,33 @@ from main import numpad numpad.inject('x') # no HSM, thanks numpad.inject('9') - numpad.inject('9') - numpad.inject('y') # settings + numpad.inject('5') + numpad.inject('y') # advanced numpad.inject('9') numpad.inject('9') numpad.inject('5') + numpad.inject('5') numpad.inject('y') # User management +if '--deriv' in sys.argv: + # Advanced > Derive Entropy + + from sim_secel import SECRETS + from sim_settings import sim_defaults + + # XPRV from spec: xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb + SECRETS['_pin1_secret'] = '011b67969d1ec69bdfeeae43213da8460ba34b92d0788c8f7bfcfa44906e8a589c3f15e5d852dc2e9ba5e9fe189a8dd2e1547badef5b563bbe6579fc6807d80ed900000000000000' + sim_defaults['chain'] = 'BTC' + + from main import numpad + numpad.inject('9') + numpad.inject('5') + numpad.inject('y') # advanced + numpad.inject('9') + numpad.inject('9') + numpad.inject('5') # up one from bottom + numpad.inject('y') # Derive Entropy + # not best place for this import hsm hsm.POLICY_FNAME = hsm.POLICY_FNAME.replace('/flash/', '') diff --git a/unix/frozen-modules/sim_settings.py b/unix/frozen-modules/sim_settings.py index c325bd2b..0313c3ee 100644 --- a/unix/frozen-modules/sim_settings.py +++ b/unix/frozen-modules/sim_settings.py @@ -82,7 +82,7 @@ print("Override XFP: " + xfp2str(sim_defaults['xfp'])) if '--seed' in sys.argv: - # --seed word1 word2 .. word24 => import seed phrase + # --seed "word1 word2 ... word24" => import that seed phrase at start from ustruct import unpack from utils import xfp2str from seed import set_seed_value