def restore_backup_workflow(device: bitbox02.BitBox02) -> None:
    """TODO: Document"""
    backups = list(device.list_backups())
    print_backups(backups)
    if not backups:
        return
    item = int(input("Choose a backup:\n"))
    backup_id, _, _ = backups[item - 1]
    print(f"ID: {backup_id}")
    if not device.restore_backup(backup_id):
        print("Restoring backup failed")
        return
    print("Please Remove SD Card")
    device.insert_or_remove_sdcard(remove=True)
def change_name(device: bitbox02.BitBox02, name: str) -> None:
    """
    Invoke change name workfow.
    """
    info = device.device_info()
    print(f"\nOld device name: {info['name']}")
    try:
        device.set_device_name(name)
    except bitbox02.UserAbortException:
        print("Aborted by user")
    else:
        print("\nSetting new device name.")
        info = device.device_info()
        print(f"\nNew device name: {info['name']}")
def _get_bitbox_and_reboot() -> devices.DeviceInfo:
    """Search for a bitbox and then reboot it into bootloader"""
    device = devices.get_any_bitbox02()

    # bitbox02 detected -> send command to reboot into bootloader to upgrade.
    def _show_pairing(code: str) -> bool:
        print("Please compare and confirm the pairing code on your BitBox02:")
        print(code)
        return True

    hid_device = hid.device()
    hid_device.open_path(device["path"])
    bitbox = BitBox02(
        transport=u2fhid.U2FHid(hid_device), device_info=device, show_pairing_callback=_show_pairing
    )
    bitbox.reboot()

    # 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 select_init_option(device: bitbox02.BitBox02) -> bool:
    """TODO: Document

    Returns:
        bool: If the user should be prompted again
    """
    # pylint: disable=too-many-branches
    print("What would you like to do?")
    print("- (1) Set up new wallet")
    print("- (2) Restore from backup")
    print("- (3) Restore from mnemonic")
    print("- (4) List device info")
    print("- (5) Reboot into bootloader")
    print("- (6) Check if SD card inserted")
    ans = input("")
    if ans == "q":
        return False
    try:
        choice = int(ans)
    except ValueError:
        print("Invalid input")
        return True
    if choice == 1:
        setup_workflow(device)
    elif choice == 2:
        restore_backup_workflow(device)
    elif choice == 3:
        if device.restore_from_mnemonic():
            print("Restore successful")
        else:
            print("Restore was NOT successful")
    elif choice == 4:
        info = device.device_info()
        print(f"\nAll info: {info}")
    elif choice == 5:
        if device.reboot():
            print("Device rebooted")
        else:
            print("User aborted")
        return False
    elif choice == 6:
        print(f"SD Card inserted: {device.check_sdcard()}")
    else:
        print("Input unknown, please try again...")
    return True
def setup_workflow(device: bitbox02.BitBox02) -> None:
    """TODO: Document"""
    device.insert_or_remove_sdcard(insert=True)
    print("SD Card Inserted")
    change_name(device, "Shifty")
    print("Please choose a password of the BitBox02. " +
          "This password will be used to unlock your BitBox02.")
    while not device.set_password():
        print("Passwords did not match. please try again")

    print("Your BitBox02 will now create a backup of your wallet...")
    print("Please confirm the date on your device.")
    if not device.create_backup():
        print("Creating the backup failed")
        exit()
    print("Backup created sucessfully")

    print("Please Remove SD Card")
    device.insert_or_remove_sdcard(remove=True)
def menu(device: bitbox02.BitBox02) -> bool:
    if not device.device_info()["initialized"]:
        return select_init_option(device)
    return select_option(device)
def select_option(device: bitbox02.BitBox02) -> bool:
    """TODO: Document
    TODO: Refactor code so that it doesn't have too many branches

    Returns:
        bool: If the user should be prompted again
    """
    # pylint: disable=too-many-branches,too-many-statements
    mnemonic_passphrase_enabled = device.device_info(
    )["mnemonic_passphrase_enabled"]
    print_menu(mnemonic_passphrase_enabled)
    ans = input("")
    if ans == "q":
        return False
    try:
        choice = int(ans)
    except ValueError:
        print("Invalid input")
        return True
    if choice == 1:
        info = device.device_info()
        print(f"\nAll info: {info}")
    elif choice == 2:
        name = input("Enter a name [Mia] (max 64 bytes): ")
        if not name:
            name = "Mia"
        change_name(device, name)
    elif choice == 3:
        print(f"Random number: {device.random_number().hex()}")
    elif choice == 4:
        print(
            "m/84'/0'/0' xpub: ",
            device.btc_pub(
                keypath=[84 + HARDENED, 0 + HARDENED, 0 + HARDENED],
                output_type=bitbox02.hww.BTCPubRequest.ZPUB,  # pylint: disable=no-member
            ),
        )
    elif choice == 5:
        # Dummy transaction to invoke a demo.
        bip44_account: int = 0 + HARDENED
        inputs: List[bitbox02.BTCInputType] = [
            {
                "prev_out_hash": b"11111111111111111111111111111111",
                "prev_out_index": 1,
                "prev_out_value": int(1e8 * 0.60005),
                "sequence": 0xFFFFFFFF,
                "keypath": [84 + HARDENED, 0 + HARDENED, bip44_account, 0, 0],
            },
            {
                "prev_out_hash": b"11111111111111111111111111111111",
                "prev_out_index": 1,
                "prev_out_value": int(1e8 * 0.60005),
                "sequence": 0xFFFFFFFF,
                "keypath": [84 + HARDENED, 0 + HARDENED, bip44_account, 0, 1],
            },
        ]
        outputs: List[bitbox02.BTCOutputType] = [
            bitbox02.BTCOutputInternal(
                keypath=[84 + HARDENED, 0 + HARDENED, bip44_account, 1, 0],
                value=int(1e8 * 1)),
            bitbox02.BTCOutputExternal(
                output_type=bitbox02.hww.P2WSH,
                output_hash=b"11111111111111111111111111111111",
                value=int(1e8 * 0.2),
            ),
        ]
        sigs = device.btc_sign(
            bitbox02.hww.BTC,
            bitbox02.hww.SCRIPT_P2WPKH,
            bip44_account=bip44_account,
            inputs=inputs,
            outputs=outputs,
        )
        for input_index, sig in sigs:
            print("Signature for input {}: {}".format(input_index, sig.hex()))
    elif choice == 6:
        print_backups(list(device.list_backups()))
    elif choice == 7:
        print("Your BitBox02 will now perform a backup check")
        backup_id = device.check_backup()
        if backup_id:
            print(f"Check successful. Backup with ID {backup_id} matches")
        else:
            print("No matching backup found")
    elif choice == 8:
        print("Your BitBox02 will now show the mnemonic seed phrase")
        print(device.show_mnemonic())
    elif choice == 9:
        if device.check_backup(silent=True) is not None:
            if input("A backup already exists, continue? Y/n: ") not in ("",
                                                                         "Y"):
                return True
        if not device.create_backup():
            print("Creating the backup failed")
        else:
            print("Backup created sucessfully")
    elif choice == 10:
        if device.reboot():
            print("Device rebooted")
            return False
        print("User aborted")
    elif choice == 11:
        print(f"SD Card inserted: {device.check_sdcard()}")
    elif choice == 12:
        mnemonic_passphrase_enabled = not mnemonic_passphrase_enabled
        try:
            device.set_mnemonic_passphrase_enabled(mnemonic_passphrase_enabled)
        except bitbox02.UserAbortException:
            print("Aborted by user")
        else:
            print("Success.")
            if mnemonic_passphrase_enabled:
                print(
                    "You can enter a mnemonic passphrase on the next unlock.")
                print("Replug your BitBox02.")
    elif choice == 13:

        def address(display: bool = False) -> str:
            return device.eth_pub(
                keypath=[44 + HARDENED, 60 + HARDENED, 0 + HARDENED, 0, 0],
                output_type=bitbox02.hww.ETHPubRequest.ADDRESS,  # pylint: disable=no-member
                display=display,
            )

        print("Ethereum address: {}".format(address(display=False)))
        address(display=True)
    elif choice == 14:
        if device.reset():
            print("Device RESET")
        else:
            print("Device NOT reset")
    else:
        print("Input unknown, please try again...")
    return True
Esempio n. 8
0
def main():
    """Main function"""
    debug = len(sys.argv) == 3 and sys.argv[2] == "debug"
    if not (len(sys.argv) == 2 or debug):
        eprint("\n\nUsage:\n\tpython load_firmware.py firmware_name.bin [debug]")
        eprint(
            "\tif debug is specified, the firmware should be unsigned, otherwise it "
            "should be signed."
        )
        return 1

    filename = sys.argv[1]
    if not debug and ".signed.bin" not in filename:
        eprint("Expecting signed firmware")
        return 1

    bootloaders = devices.get_bitbox02_devices(devices.BOOTLOADER)
    bitboxes = devices.get_bitbox02_devices()

    def _wait_for_bootloaders():
        while True:
            bootloaders = devices.get_bitbox02_devices(devices.BOOTLOADER)
            if bootloaders:
                return bootloaders
            sys.stdout.write(".")
            sleep(1)

    if not bootloaders:
        if len(bitboxes) != 1:
            eprint(
                "No bitbox02 bootloader detected. Insert exactly one bootloader or "
                "bitbox02 device."
            )
            return 1

        # bitbox02 detected -> send command to reboot into bootloader to upgrade.
        def show_pairing(code):
            eprint("Please compare and confirm the pairing code on your BitBox02:")
            eprint(code)

        bitbox = BitBox02(device_path=bitboxes[0]["path"], show_pairing_callback=show_pairing)
        bitbox.reboot()
        bootloaders = _wait_for_bootloaders()

    if len(bootloaders) > 1:
        eprint("Multiple bootloaders detected. Only one supported")
        return 1

    pprint.pprint(bootloaders[0])

    bootloader = Bootloader(bootloaders[0])

    with open(filename, "rb") as file:
        firmware = file.read()

    def progress(perc):
        sys.stdout.write(f"{perc*100:.02f}%\r")

    if bootloader.erased():
        print("device contains NO firmware")
    else:
        print("firmware version: %d\nsigning pubkeys version: %d" % bootloader.versions())
        firmware_hash, signing_keydata_hash = bootloader.get_hashes()
        print("firmware hash:", firmware_hash.hex())
        print("signing keydata hash:", signing_keydata_hash.hex())

    if debug:
        bootloader.flash_unsigned_firmware(firmware, progress)
    else:
        bootloader.flash_signed_firmware(firmware, progress)
    print()  # print a newline

    sleep(1)  # Pause to show the upgrade finished at 100%
    bootloader.reboot()
    return 0