async def storage_menu(self): """Manage storage""" buttons = [ # id, text (None, "Key management"), (0, "Save key to flash"), (1, "Load key from flash"), (2, "Delete key from flash"), ] # we stay in this menu until back is pressed while True: # wait for menu selection menuitem = await self.show(Menu(buttons, last=(255, None))) # process the menu button: # back button if menuitem == 255: return elif menuitem == 0: await self.save_mnemonic() await self.show( Alert("Success!", "Your key is stored in flash now.", button_text="OK")) elif menuitem == 1: await self.load_mnemonic() await self.show( Alert("Success!", "Your key is loaded.", button_text="OK")) elif menuitem == 2: await self.delete_mnemonic() await self.show( Alert("Success!", "Your key is deleted from flash.", button_text="OK"))
async def storage_menu(self): """Manage storage""" enabled = self.connection.isCardInserted() buttons = [ # id, text (None, "Smartcard storage"), (0, "Save key to the card", enabled), (1, "Load key from the card", enabled), (2, "Delete key from the card", enabled), (3, "Use a different card", enabled) ] # we stay in this menu until back is pressed while True: note = "Card fingerprint: %s" % self.hexid # wait for menu selection menuitem = await self.show( Menu(buttons, note=note, last=(255, None))) # process the menu button: # back button if menuitem == 255: return elif menuitem == 0: await self.save_mnemonic() await self.show( Alert( "Success!", "Your key is stored on the smartcard now.", button_text="OK", )) elif menuitem == 1: await self.load_mnemonic() await self.show( Alert("Success!", "Your key is loaded.", button_text="OK")) elif menuitem == 2: await self.delete_mnemonic() await self.show( Alert( "Success!", "Your key is deleted from the smartcard.", button_text="OK", )) elif menuitem == 3: if await self.show( Prompt( "Switching the smartcard", "To use a different smartcard you need " "to provide a PIN for current one first!\n\n" "Continue?")): self.lock() await self.unlock() self.lock() self.applet.close_secure_channel() await self.show( Alert("Please swap the card", "Now you can insert another card and set it up.", button_text="Continue")) await self.check_card(check_pin=True) await self.unlock()
async def settings_menu(self, show_screen, keystore): title = "QR scanner" controls = [{ "label": "Enable QR scanner", "hint": "Enable or disable QR scanner and remove corresponding button from the main menu", "value": self.settings.get("enabled", True) }, { "label": "Sound", "hint": "To beep or not to beep?", "value": self.settings.get("sound", True) }, { "label": "Aim light", "hint": "Laser eyes!", "value": self.settings.get("aim", True) }, { "label": "Flashlight", "hint": "Can create blicks on the screen", "value": self.settings.get("light", False) }] scr = HostSettings(controls, title=title) res = await show_screen(scr) if res: enabled, sound, aim, light = res self.settings = { "enabled": enabled, "aim": aim, "light": light, "sound": sound, } self.save_settings(keystore) self.configure() await show_screen( Alert("Success!", "\n\nSettings updated!", button_text="Close"))
async def show_xpub(self, derivation, show_screen): self.show_loader(title="Deriving the key...") derivation = derivation.rstrip("/") net = NETWORKS[self.network] xpub = self.keystore.get_xpub(derivation) ver = bip32.detect_version(derivation, default="xpub", network=net) canonical = xpub.to_base58(net["xpub"]) slip132 = xpub.to_base58(ver) if slip132 == canonical: slip132 = None fingerprint = hexlify(self.keystore.fingerprint).decode() prefix = "[%s%s]" % ( fingerprint, derivation[1:], ) res = await show_screen( XPubScreen(xpub=canonical, slip132=slip132, prefix=prefix)) if res: fname = "%s-%s.txt" % (fingerprint, derivation[2:].replace( "/", "-")) if not platform.is_sd_present(): raise AppError("SD card is not present") platform.mount_sdcard() with open(platform.fpath("/sd/%s" % fname), "w") as f: f.write(res) platform.unmount_sdcard() await show_screen( Alert("Saved!", "Extended public key is saved to the file:\n\n%s" % fname, button_text="Close"))
async def setup_pin(self, get_word=None): """ PIN setup screen - first choose, then confirm If PIN codes are the same -> return the PIN If not -> try again """ scr = PinScreen( title="Choose your PIN code", note="Remember these words," "they will stay the same on this device.", get_word=self.get_auth_word, subtitle=self.pin_subtitle, ) pin1 = await self.show(scr) scr = PinScreen( title="Confirm your PIN code", note="Remember these words," "they will stay the same on this device.", get_word=self.get_auth_word, subtitle=self.pin_subtitle, ) pin2 = await self.show(scr) # check if PIN is the same if pin1 == pin2: return pin1 # if not - show an error await self.show(Alert("Error!", "PIN codes are different!")) return await self.setup_pin(get_word)
async def export_mnemonic(self): if await self.show( Prompt( "Warning", "You need to confirm your PIN code " "to export your recovery phrase.\n\n" "Your recovery phrase will be saved " "to the SD card as plain text.\n\n" "Anybody who has access to this SD card " "will be able to read your recovery phrase!\n\n" "Continue?")): self.lock() await self.unlock() filename = "seed-export-%s.txt" % self.mnemonic.split()[0] filepath = "%s/%s" % (self.sdpath, filename) if not platform.is_sd_present(): raise KeyStoreError("SD card is not present") platform.mount_sdcard() with open(filepath, "w") as f: f.write("bip39: ") f.write(self.mnemonic) platform.unmount_sdcard() await self.show( Alert("Success!", "Your seed is exported.\n\nName: %s" % filename, button_text="OK"))
async def show_xpub(self, derivation, show_screen): self.show_loader(title="Deriving the key...") derivation = derivation.rstrip("/") net = NETWORKS[self.network] xpub = self.keystore.get_xpub(derivation) ver = bip32.detect_version(derivation, default="xpub", network=net) canonical = xpub.to_base58(net["xpub"]) slip132 = xpub.to_base58(ver) if slip132 == canonical: slip132 = None fingerprint = hexlify(self.keystore.fingerprint).decode() prefix = "[%s%s]" % ( fingerprint, derivation[1:], ) res = await show_screen(XPubScreen(xpub=canonical, slip132=slip132, prefix=prefix)) if res == XPubScreen.CREATE_WALLET: await self.create_wallet(derivation, canonical, prefix, ver, show_screen) elif res: filename = "%s-%s.txt" % (fingerprint, derivation[2:].replace("/", "-")) self.write_file(filename, res) await show_screen( Alert("Saved!", "Extended public key is saved to the file:\n\n%s" % filename, button_text="Close") )
async def change_pin(self): # get_auth_word function can generate words from part of the PIN old_pin = await self.get_pin(title="First enter your old PIN code") # check pin - will raise if not valid self._unlock(old_pin) new_pin = await self.setup_pin() self._change_pin(old_pin, new_pin) await self.show( Alert("Success!", "PIN code is sucessfully changed!", button_text="OK") )
async def storage_menu(self): """Manage storage""" buttons = [ # id, text (None, "Manage keys on SD card and internal flash"), (0, "Save key"), (1, "Load key"), (2, "Delete key"), ] # disabled if SD card is not present buttons.append( (3, "Export recovery phrase to SD", platform.is_sd_present())) # we stay in this menu until back is pressed while True: # wait for menu selection menuitem = await self.show(Menu(buttons, last=(255, None))) # process the menu button: # back button if menuitem == 255: return elif menuitem == 0: filename = await self.save_mnemonic() if filename: await self.show( Alert("Success!", "Your key is stored now.\n\nName: %s" % filename, button_text="OK")) elif menuitem == 1: if await self.load_mnemonic(): await self.show( Alert("Success!", "Your key is loaded.", button_text="OK")) elif menuitem == 2: if await self.delete_mnemonic(): await self.show( Alert("Success!", "Your key is deleted.", button_text="OK")) elif menuitem == 3: await self.export_mnemonic()
async def storage_menu(self): """Manage storage and display of the recovery phrase""" enabled = self.connection.isCardInserted() buttons = [ # id, text (None, "Smartcard storage"), (0, "Save key to the card", enabled), (1, "Load key from the card", enabled), (2, "Delete key from the card", enabled), (3, "Show recovery phrase"), ] # we stay in this menu until back is pressed while True: # wait for menu selection menuitem = await self.show(Menu(buttons, last=(255, None))) # process the menu button: # back button if menuitem == 255: return elif menuitem == 0: await self.save_mnemonic() await self.show( Alert( "Success!", "Your key is stored on the smartcard now.", button_text="OK", )) elif menuitem == 1: await self.load_mnemonic() await self.show( Alert("Success!", "Your key is loaded.", button_text="OK")) elif menuitem == 2: await self.delete_mnemonic() await self.show( Alert( "Success!", "Your key is deleted from the smartcard.", button_text="OK", )) elif menuitem == 3: await self.show(MnemonicScreen(self.mnemonic))
async def storage_menu(self): """Manage storage and display of the recovery phrase""" buttons = [ # id, text (None, "Manage keys on SD card and internal flash"), (0, "Save key"), (1, "Load key"), (2, "Delete key"), (None, "Other"), (3, "Show recovery phrase"), ] # we stay in this menu until back is pressed while True: # wait for menu selection menuitem = await self.show(Menu(buttons, last=(255, None))) # process the menu button: # back button if menuitem == 255: return elif menuitem == 0: if await self.save_mnemonic(): await self.show( Alert("Success!", "Your key is stored now.", button_text="OK")) elif menuitem == 1: if await self.load_mnemonic(): await self.show( Alert("Success!", "Your key is loaded.", button_text="OK")) elif menuitem == 2: if await self.delete_mnemonic(): await self.show( Alert("Success!", "Your key is deleted.", button_text="OK")) elif menuitem == 3: await self.show_mnemonic()
async def settings_menu(self, show_screen, keystore): title = self.settings_button or "Settings" controls = [{ "label": "Enable " + title, "hint": "This setting will completely enable or disable this communication channel and remove corresponding button from the main menu", "value": self.settings["enabled"] }] scr = HostSettings(controls, title=title) res = await show_screen(scr) if res: self.settings["enabled"] = res[0] self.save_settings(keystore) await show_screen( Alert("Success!", "\n\nSettings updated!", button_text="Close"))
async def change_pin(self): # get_auth_word function can generate words from part of the PIN old_pin = await self.get_pin(title="First enter your old PIN code", with_cancel=True) if old_pin is None: return # check pin - will raise if not valid self.show_loader("Verifying PIN code...") self._unlock(old_pin) new_pin = await self.setup_pin() self.show_loader("Setting new PIN code...") self._change_pin(old_pin, new_pin) await self.show( Alert("Success!", "PIN code is successfully changed!", button_text="OK"))
async def export_menu(self, show_screen): while True: buttons = [ (None, "Export options"), (1, "Show QR code"), (2, "Save to SD card", platform.is_sd_present()), ] menuitem = await show_screen( Menu(buttons, last=(255, None), title="Export wallet %s" % self.name)) desc = add_checksum(str(self.descriptor.branch(0))) obj = { "descriptor": desc, "label": self.name, } if self.descriptor.num_branches > 2: obj["branches"] = [ add_checksum(str(self.descriptor.branch(i))) for i in range(self.descriptor.num_branches) ] if menuitem == 1: await show_screen( QRAlert( title="Export wallet %s" % self.name, qr_width=450, message= "Scan this QR code with compatible software wallet", qr_message=json.dumps(obj))) elif menuitem == 2: if not platform.is_sd_present(): raise WalletError("SD card is not present") platform.mount_sdcard() fname = "/sd/%s.json" % self.name with open(platform.fpath(fname), "w") as f: json.dump(obj, f) platform.unmount_sdcard() await show_screen( Alert("Success!", "Wallet descriptor is written to\n\n%s" % fname)) else: return
async def init(self, show_fn): """ Waits for keystore media and loads internal secret and PIN state """ self.show = show_fn platform.maybe_mkdir(self.path) self.load_secret(self.path) self.load_state() # check if init is called for the first time # and we have less than max PIN attempts if (not self.initialized and self.pin_attempts_left != self.pin_attempts_max): scr = Alert("Warning!", "You only have %d of %d attempts\n" "to enter correct PIN code!" % (self.pin_attempts_left, self.pin_attempts_max), button_text="OK") await self.show(scr) self.initialized = True
async def show_card_info(self): note = "Card fingerprint: %s" % self.hexid version = "%s v%s" % (self.applet.NAME, self.applet.version) platform = self.applet.platform data = self.applet.get_secret() key_saved = len(data) > 0 encrypted = True decryptable = True same_mnemonic = False if key_saved: try: d, encrypted = self.parse_data(data) if "entropy" in d: self._is_key_saved = True same_mnemonic = (self.mnemonic == bip39.mnemonic_from_bytes( d["entropy"])) except KeyStoreError as e: decryptable = False # yes = lv.SYMBOL.OK+" Yes" # no = lv.SYMBOL.CLOSE+" No" yes = "Yes" no = "No" props = [ "\n#7f8fa4 PLATFORM #", "Implementation: %s" % platform, "Version: %s" % version, "\n#7f8fa4 KEY INFO: #", "Key saved: " + (yes if key_saved else no), ] if key_saved: if decryptable: props.append("Same as current key: " + (yes if same_mnemonic else no)) props.append("Encrypted: " + (yes if encrypted else no)) if encrypted: props.append("Decryptable: " + (yes if decryptable else no)) scr = Alert("Smartcard info", "\n\n".join(props), note=note) scr.message.set_recolor(True) await self.show(scr)
async def menu(self, show_screen, show_all=False): net = NETWORKS[self.network] coin = net["bip32"] if not show_all: buttons = [ (None, "Recommended"), ("m/84h/%dh/%dh" % (coin, self.account), "Single key"), ("m/48h/%dh/%dh/2h" % (coin, self.account), "Multisig"), (None, "Other keys"), (0, "Show more keys"), (2, "Change account number"), (1, "Enter custom derivation"), (3, "Export all keys to SD"), ] else: buttons = [ (None, "Recommended"), ( "m/84h/%dh/%dh" % (coin, self.account), "Single Native Segwit\nm/84h/%dh/%dh" % (coin, self.account) ), ( "m/48h/%dh/%dh/2h" % (coin, self.account), "Multisig Native Segwit\nm/48h/%dh/%dh/2h" % (coin, self.account), ), (None, "Other keys"), ] if self.is_taproot_enabled: buttons.append(( "m/86h/%dh/%dh" % (coin, self.account), "Single Taproot\nm/86h/%dh/%dh" % (coin, self.account) )) buttons.extend([ ( "m/49h/%dh/%dh" % (coin, self.account), "Single Nested Segwit\nm/49h/%dh/%dh" % (coin, self.account) ), ( "m/48h/%dh/%dh/1h" % (coin, self.account), "Multisig Nested Segwit\nm/48h/%dh/%dh/1h" % (coin, self.account), ), ]) # wait for menu selection menuitem = await show_screen(Menu(buttons, last=(255, None), title="Select the key", note="Current account number: %d" % self.account)) # process the menu button: # back button if menuitem == 255: return False elif menuitem == 0: return await self.menu(show_screen, show_all=True) elif menuitem == 1: der = await show_screen(DerivationScreen()) if der is not None: await self.show_xpub(der, show_screen) return True elif menuitem == 3: file_format = await self.save_menu(show_screen) if file_format: filename = self.save_all_to_sd(file_format) await show_screen( Alert("Saved!", "Public keys are saved to the file:\n\n%s" % filename, button_text="Close") ) elif menuitem == 2: account = await show_screen(NumericScreen(current_val=str(self.account))) if account and int(account) > 0x80000000: raise AppError('Account number too large') try: self.account = int(account) except: self.account = 0 return await self.menu(show_screen) else: await self.show_xpub(menuitem, show_screen) return True return False
async def storage_menu(self): """Manage storage""" enabled = self.connection.isCardInserted() buttons = [ # id, text, enabled, color (None, "Smartcard storage"), (0, "Save key to the card", enabled), (1, "Load key from the card", enabled and self.is_key_saved), (2, "Delete key from the card", enabled and self.is_key_saved), (3, "Use a different card", enabled), (4, lv.SYMBOL.SETTINGS + " Get card info", enabled), # (5, lv.SYMBOL.TRASH + " Wipe the card", enabled, 0x951E2D), ] # we stay in this menu until back is pressed while True: # check updated status buttons[2] = (1, "Load key from the card", enabled and self.is_key_saved) buttons[3] = (2, "Delete key from the card", enabled and self.is_key_saved) note = "Card fingerprint: %s" % self.hexid # wait for menu selection menuitem = await self.show( Menu(buttons, note=note, last=(255, None))) # process the menu button: # back button if menuitem == 255: return elif menuitem == 0: await self.save_mnemonic() await self.show( Alert( "Success!", "Your key is stored on the smartcard now.", button_text="OK", )) elif menuitem == 1: await self.load_mnemonic() await self.show( Alert("Success!", "Your key is loaded.", button_text="OK")) elif menuitem == 2: if await self.show( Prompt("Are you sure?", "\n\nDelete the key from the card?")): await self.delete_mnemonic() await self.show( Alert( "Success!", "Your key is deleted from the smartcard.", button_text="OK", )) elif menuitem == 3: if await self.show( Prompt( "Switching the smartcard", "To use a different smartcard you need " "to provide a PIN for current one first!\n\n" "Continue?")): self.lock() await self.unlock() self.lock() self.applet.close_secure_channel() self._userkey = None await self.show( Alert("Please swap the card", "Now you can insert another card and set it up.", button_text="Continue")) await self.check_card(check_pin=True) await self.unlock() elif menuitem == 4: await self.show_card_info() else: raise KeyStoreError("Invalid menu")