async def remember_bip39_passphrase(): # Compute current xprv and switch to using that as root secret. import stash from common 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 clear_seed(restart=True): from common import dis, pa, settings import utime import version dis.fullscreen('Erasing Seed...') # clear settings associated with this key, since it will be no more settings.reset() # save a blank secret (all zeros is a special case, detected by bootloader) nv = bytes(AE_SECRET_LEN) pa.change(new_secret=nv) # wipe the long secret too nv = bytes(AE_LONG_SECRET_LEN) pa.ls_change(nv) if restart: dis.fullscreen('Restarting...') utime.sleep(1) # security: need to reboot to really be sure to clear the secrets from main memory. from machine import reset reset() # EOF
def save_wallet_seed(seed_bits): from common import dis, pa, settings print('save_wallet_seed 1') # encode it for our limited secret space nv = SecretStash.encode(seed_bits=seed_bits) print('save_wallet_seed 2: nv={}'.format(b2a_hex(nv))) dis.fullscreen('Saving Wallet...') pa.change(new_secret=nv) print('save_wallet_seed 3') # re-read settings since key is now different # - also captures xfp, xpub at this point pa.new_main_secret(nv) print('save_wallet_seed 4') # check and reload secret pa.reset() print('save_wallet_seed 5') pa.login() print('save_wallet_seed 6')
async def restore_from_dict(vals): # Restore from a dict of values. Already JSON decoded. # Reboot on success, return string on failure from common import pa, dis, settings from pincodes import SE_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(SE_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: 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 ('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()
async def show(self): while True: print('show: state={}'.format(self.state)) if self.state == self.ENTER_PIN1: self.pin1[self.round] = await ux_enter_pin( title='Security Code', heading='{} Security Code'.format('Old' if self.round == 0 else 'New')) if self.pin1[self.round] != None and len( self.pin1[self.round]) >= MIN_PIN_PART_LEN: if self.round == 1: self.goto(self.SHOW_ANTI_PHISHING_WORDS) else: self.goto(self.ENTER_PIN2) elif self.state == self.SHOW_ANTI_PHISHING_WORDS: start = utime.ticks_us() words = pincodes.PinAttempt.anti_phishing_words( self.pin1[self.round].encode()) end = utime.ticks_us() result = await ux_show_word_list('Security Words', words, heading1='Remember these', heading2='Security Words:', left_btn='BACK', right_btn='OK') if result == 'x': self.pin1[self.round] = None self.goto(self.ENTER_PIN1) else: self.goto(self.ENTER_PIN2) elif self.state == self.ENTER_PIN2: self.pin2[self.round] = await ux_enter_pin( title='Login PIN', heading='{} Login PIN'.format('Old' if self.round == 0 else 'New')) if self.pin2[self.round] != None and len( self.pin2[self.round]) >= MIN_PIN_PART_LEN: if self.round == 0: self.round = 1 self.goto(self.ENTER_PIN1) else: self.goto(self.CHANGE_PIN) elif self.state == self.CHANGE_PIN: try: print('pin1={} pin2={}'.format(self.pin1, self.pin2)) args = {} args['old_pin'] = (self.pin1[0] + self.pin2[0]).encode() args['new_pin'] = (self.pin1[1] + self.pin2[1]).encode() print('pa.change: args={}'.format(args)) pa.change(**args) self.goto(self.CHANGE_SUCCESS) except Exception as err: print('err={}'.format(err)) self.goto(self.CHANGE_FAILED) elif self.state == self.CHANGE_FAILED: result = await ux_show_story( 'Unable to change PIN. The old PIN you entered was incorrect.', title='PIN Error', right_btn='RETRY') if result == 'y': self.pin1 = [None, None] self.pin2 = [None, None] self.round = 0 self.goto(self.ENTER_PIN1) else: return elif self.state == self.CHANGE_SUCCESS: dis.fullscreen('PIN changed') utime.sleep(1) return else: while True: print('ERROR: Should never hit this else case!') from uasyncio import sleep_ms await sleep_ms(1000)