def _find_and_open_usb_bitbox02( use_cache: bool) -> Tuple[devices.DeviceInfo, TransportLayer]: """ Connects to a BitBox02 bootloader over USB. If the BitBox02 is currently running a firmware, it will be rebooted and this function will connect to the bootloader when it shows up. """ bootloader_device = None try: bootloader_device = devices.get_any_bitbox02_bootloader() except TooManyFoundException: eprint( "Found multiple bb02 bootloader standard editions. Only one supported." ) sys.exit(1) except NoneFoundException: pass if bootloader_device is None: try: bootloader_device = _get_bitbox_and_reboot(use_cache) except TooManyFoundException: eprint("Found multiple bitboxes. Only one supported.") sys.exit(1) except NoneFoundException: eprint("Neither bootloader nor bitbox found.") sys.exit(1) pprint.pprint(bootloader_device) hid_device = hid.device() hid_device.open_path(bootloader_device["path"]) return bootloader_device, u2fhid.U2FHid(hid_device)
def _get_bitbox_and_reboot(use_cache: bool) -> devices.DeviceInfo: """Search for a bitbox and then reboot it into bootloader""" device = devices.get_any_bitbox02() class NoiseConfig(util.NoiseConfigUserCache): """NoiseConfig extends NoiseConfigUserCache""" def __init__(self) -> None: super().__init__("shift/load_firmware") def show_pairing(self, code: str, device_response: Callable[[], bool]) -> bool: print( "Please compare and confirm the pairing code on your BitBox02:" ) print(code) return device_response() class NoiseConfigNoCache(bitbox_api_protocol.BitBoxNoiseConfig): """NoiseConfig extends BitBoxNoiseConfig""" def show_pairing(self, code: str, device_response: Callable[[], bool]) -> bool: print( "Please compare and confirm the pairing code on your BitBox02:" ) print(code) return device_response() if use_cache: config: bitbox_api_protocol.BitBoxNoiseConfig = NoiseConfig() else: config = NoiseConfigNoCache() hid_device = hid.device() hid_device.open_path(device["path"]) bitbox = BitBox02(transport=u2fhid.U2FHid(hid_device), device_info=device, noise_config=config) if not bitbox.reboot(): raise RuntimeError("User aborted") # wait for it to reboot while True: try: bootloader_device = devices.get_any_bitbox02_bootloader() except NoneFoundException: sys.stdout.write(".") sys.stdout.flush() sleep(1) continue return bootloader_device
def __init__(self, path: str, password: str = "", expert: bool = False) -> None: """ Initializes a new BitBox02 client instance. """ super().__init__(path, password=password, expert=expert) if password: raise BadArgumentError( "The BitBox02 does not accept a passphrase from the host. Please enable the passphrase option and enter the passphrase on the device during unlock." ) hid_device = hid.device() hid_device.open_path(path.encode()) self.transport = u2fhid.U2FHid(hid_device) self.device_path = path # use self.init() to access self.bb02. self.bb02: Optional[bitbox02.BitBox02] = None self.noise_config: BitBoxNoiseConfig = CLINoiseConfig()
def pairing_dialog(self): def pairing_step(code: str, device_response: Callable[[], bool]) -> bool: msg = "Please compare and confirm the pairing code on your BitBox02:\n" + code self.handler.show_message(msg) try: res = device_response() except: # Close the hid device on exception with self.device_manager().hid_lock: hid_device.close() raise finally: self.handler.finished() return res def exists_remote_static_pubkey(pubkey: bytes) -> bool: bitbox02_config = self.config.get("bitbox02") noise_keys = bitbox02_config.get("remote_static_noise_keys") if noise_keys is not None: if pubkey.hex() in [noise_key for noise_key in noise_keys]: return True return False def set_remote_static_pubkey(pubkey: bytes) -> None: if not exists_remote_static_pubkey(pubkey): bitbox02_config = self.config.get("bitbox02") if bitbox02_config.get("remote_static_noise_keys") is not None: bitbox02_config["remote_static_noise_keys"].append(pubkey.hex()) else: bitbox02_config["remote_static_noise_keys"] = [pubkey.hex()] self.config.set_key("bitbox02", bitbox02_config) def get_noise_privkey() -> Optional[bytes]: bitbox02_config = self.config.get("bitbox02") privkey = bitbox02_config.get("noise_privkey") if privkey is not None: return bytes.fromhex(privkey) return None def set_noise_privkey(privkey: bytes) -> None: bitbox02_config = self.config.get("bitbox02") bitbox02_config["noise_privkey"] = privkey.hex() self.config.set_key("bitbox02", bitbox02_config) def attestation_warning() -> None: self.handler.show_error( "The BitBox02 attestation failed.\nTry reconnecting the BitBox02.\nWarning: The device might not be genuine, if the\n problem persists please contact Shift support.", blocking=True ) class NoiseConfig(bitbox_api_protocol.BitBoxNoiseConfig): """NoiseConfig extends BitBoxNoiseConfig""" def show_pairing(self, code: str, device_response: Callable[[], bool]) -> bool: return pairing_step(code, device_response) def attestation_check(self, result: bool) -> None: if not result: attestation_warning() def contains_device_static_pubkey(self, pubkey: bytes) -> bool: return exists_remote_static_pubkey(pubkey) def add_device_static_pubkey(self, pubkey: bytes) -> None: return set_remote_static_pubkey(pubkey) def get_app_static_privkey(self) -> Optional[bytes]: return get_noise_privkey() def set_app_static_privkey(self, privkey: bytes) -> None: return set_noise_privkey(privkey) if self.bitbox02_device is None: with self.device_manager().hid_lock: hid_device = hid.device() hid_device.open_path(self.bitbox_hid_info["path"]) bitbox02_device = bitbox02.BitBox02( transport=u2fhid.U2FHid(hid_device), device_info=self.bitbox_hid_info, noise_config=NoiseConfig(), ) try: bitbox02_device.check_min_version() except FirmwareVersionOutdatedException: raise self.bitbox02_device = bitbox02_device self.fail_if_not_initialized()
def connect_to_usb_bitbox(debug: bool, use_cache: bool) -> int: """ Connects and runs the main menu on a BitBox02 connected over USB. """ try: bitbox = devices.get_any_bitbox02() except devices.TooManyFoundException: print("Multiple bitboxes detected. Only one supported") return 1 except devices.NoneFoundException: try: bootloader = devices.get_any_bitbox02_bootloader() except devices.TooManyFoundException: print("Multiple bitbox bootloaders detected. Only one supported") return 1 except devices.NoneFoundException: print("Neither bitbox nor bootloader found.") return 1 else: hid_device = hid.device() hid_device.open_path(bootloader["path"]) bootloader_connection = bitbox02.Bootloader( u2fhid.U2FHid(hid_device), bootloader) boot_app = SendMessageBootloader(bootloader_connection) return boot_app.run() else: def show_pairing(code: str, device_response: Callable[[], bool]) -> bool: print( "Please compare and confirm the pairing code on your BitBox02:" ) print(code) if not device_response(): return False return input("Accept pairing? [y]/n: ").strip() != "n" class NoiseConfig(util.NoiseConfigUserCache): """NoiseConfig extends NoiseConfigUserCache""" def __init__(self) -> None: super().__init__("shift/send_message") def show_pairing(self, code: str, device_response: Callable[[], bool]) -> bool: return show_pairing(code, device_response) def attestation_check(self, result: bool) -> None: if result: print("Device attestation PASSED") else: print("Device attestation FAILED") class NoiseConfigNoCache(bitbox_api_protocol.BitBoxNoiseConfig): """NoiseConfig extends BitBoxNoiseConfig""" def show_pairing(self, code: str, device_response: Callable[[], bool]) -> bool: return show_pairing(code, device_response) def attestation_check(self, result: bool) -> None: if result: print("Device attestation PASSED") else: print("Device attestation FAILED") if use_cache: config: bitbox_api_protocol.BitBoxNoiseConfig = NoiseConfig() else: config = NoiseConfigNoCache() hid_device = hid.device() hid_device.open_path(bitbox["path"]) bitbox_connection = bitbox02.BitBox02( transport=u2fhid.U2FHid(hid_device), device_info=bitbox, noise_config=config) try: bitbox_connection.check_min_version() except FirmwareVersionOutdatedException as exc: print("WARNING: ", exc) if debug: print("Device Info:") pprint.pprint(bitbox) return SendMessage(bitbox_connection, debug).run()