async def remember_bip39_passphrase(): # Compute current xprv and switch to using that as root secret. import stash from main import dis, pa if not stash.bip39_passphrase: if not await ux_confirm( '''You do not have a BIP39 passphrase set right now, so this command does little except forget the seed words. It does not enhance security.''' ): return 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) dis.fullscreen('Saving...') pa.change(new_secret=nv) # re-read settings since key is now different # - also captures xfp, xpub at this point pa.new_main_secret(nv) # check and reload secret pa.reset() pa.login()
async def remember_bip39_passphrase(): # Compute current xprv and switch to using that as root secret. import stash from main import dis, pa 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 stash.bip39_passphrase = '' dis.fullscreen('Saving...') pa.change(new_secret=nv) # re-read settings since key is now different # - also captures xfp, xpub at this point pa.new_main_secret(nv) # check and reload secret pa.reset() pa.login()
def set_seed_value(words): # Save the seed words into secure element, and reboot. BIP39 password # is not set at this point (empty string) ok = tcc.bip39.check(' '.join(words)) assert ok, "seed check: %r" % words # map words to bip39 wordlist indices data = [tcc.bip39.lookup_word(w) for w in words] # map to packed binary representation. val = 0 for v in data: val <<= 11 val |= v # remove the checksum part vlen = (len(words) * 4) // 3 val >>= (len(words) // 3) # convert to bytes seed = val.to_bytes(vlen, 'big') assert len(seed) == vlen from main import dis, pa, settings # encode it for our limited secret space nv = SecretStash.encode(seed_phrase=seed) dis.fullscreen('Applying...') pa.change(new_secret=nv) # re-read settings since key is now different # - also captures xfp, xpub at this point pa.new_main_secret(nv) # check and reload secret pa.reset() pa.login()
async def remember_bip39_passphrase(): # Compute current xprv and switch to using that as root secret. import stash from main import dis, pa dis.fullscreen('Check...') with stash.SensitiveValues() as sv: nv = SecretStash.encode(xprv=sv.node) # Important: won't write new XFP to nvram if pw still set stash.bip39_passphrase = '' dis.fullscreen('Saving...') pa.change(new_secret=nv) # re-read settings since key is now different # - also captures xfp, xpub at this point pa.new_main_secret(nv) # check and reload secret pa.reset() pa.login()
async def restore_from_dict(vals): # Restore from a dict of values. Already JSON decoded. # Reboot on success, return string on failure from main import pa, dis, settings from pincodes import AE_SECRET_LEN #print("Restoring from: %r" % vals) # step1: the private key # - prefer raw_secret over other values # - TODO: fail back to other values try: chain = chains.get_chain(vals.get('chain', 'BTC')) assert 'raw_secret' in vals raw = bytearray(AE_SECRET_LEN) rs = vals.pop('raw_secret') if len(rs) % 2: rs += '0' x = a2b_hex(rs) raw[0:len(x)] = x # check we can decode this right (might be different firmare) opmode, bits, node = stash.SecretStash.decode(raw) assert node # verify against xprv value (if we have it) if 'xprv' in vals: check_xprv = chain.serialize_private(node) assert check_xprv == vals['xprv'], 'xprv mismatch' except Exception as e: return ('Unable to decode raw_secret and ' 'restore the seed value!\n\n\n' + str(e)) ls = None if ('long_secret' in vals) and version.has_608: try: ls = a2b_hex(vals.pop('long_secret')) except Exception as exc: sys.print_exception(exc) # but keep going. dis.fullscreen("Saving...") dis.progress_bar_show(.25) # clear (in-memory) settings and change also nvram key # - also captures xfp, xpub at this point pa.change(new_secret=raw) # force the right chain pa.new_main_secret(raw, chain) # updates xfp/xpub # NOTE: don't fail after this point... they can muddle thru w/ just right seed if ls is not None: try: pa.ls_change(ls) except Exception as exc: sys.print_exception(exc) # but keep going # restore settings from backup file for idx, k in enumerate(vals): dis.progress_bar_show(idx / len(vals)) if not k.startswith('setting.'): continue if k == 'xfp' or k == 'xpub': continue settings.set(k[8:], vals[k]) # write out settings.save() if version.has_fatram and ('hsm_policy' in vals): import hsm hsm.restore_backup(vals['hsm_policy']) await ux_show_story( 'Everything has been successfully restored. ' 'We must now reboot to install the ' 'updated settings and/or seed.', title='Success!') from machine import reset reset()
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)
# 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)))
# (c) Copyright 2020 by Coinkite Inc. This file is covered by license found in COPYING-CC. # # load up the simulator w/ indicated test master key 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 rs = main.RAW_SECRET print("New raw secret: %s" % b2a_hex(rs)) if 1: settings.current = sim_defaults settings.overrides.clear() settings.set('chain', 'XTN') pa.change(new_secret=rs) pa.new_main_secret(rs) print("New key in effect: %s" % settings.get('xpub', 'MISSING')) print("Fingerprint: %s" % xfp2str(settings.get('xfp', 0)))