Beispiel #1
0
def connect_to_usart_bitboxbase(debug: bool,
                                serial_port: usart.SerialPort) -> int:
    """
    Connects and runs the main menu over a BitBoxBase connected
    over UART.
    """
    print("Trying to connect to BitBoxBase firmware...")
    bootloader_device: devices.DeviceInfo = get_bitboxbase_default_device(
        serial_port.port)

    def show_pairing(code: str) -> bool:
        print("(Pairing should be automatic) Pairing code:")
        print(code)
        return True

    def attestation_check(result: bool) -> None:
        if result:
            print("Device attestation PASSED")
        else:
            print("Device attestation FAILED")

    try:
        transport = usart.U2FUsart(serial_port)
        base_dev = BitBoxBase(
            transport,
            bootloader_device,
            show_pairing_callback=show_pairing,
            attestation_check_callback=attestation_check,
        )
        if debug:
            print("Device Info:")
            pprint.pprint(base_dev)
        return SendMessageBitBoxBase(base_dev, debug).run()
    except usart.U2FUsartErrorResponse as err:
        if err.error_code != usart.U2FUsartErrorResponse.ENDPOINT_UNAVAILABLE:
            raise
    except usart.U2FUsartTimeoutError:
        print("Timed out. Maybe the device is not connected?", file=sys.stderr)
        return 1

    print("BitBox unavailable. Starting bootloader connection.")
    transport = usart.U2FUsart(serial_port)
    bootloader = bitbox02.Bootloader(transport, bootloader_device)
    return SendMessageBootloader(bootloader).run()
def main() -> int:
    """Main function"""
    parser = argparse.ArgumentParser(
        description="Tool for flashing a new firmware on BitBox devices."
    )
    parser.add_argument("--debug", action="store_true", help="Flash a debug (unsigned) firmware.")
    parser.add_argument(
        "--usart",
        action="store",
        help="Flash firmware using U2F-over-UART (BitBoxBase), with the specified serial port.",
    )
    parser.add_argument("firmware", nargs=1, help="Firmware to flash.")
    args = parser.parse_args()

    if not args.debug and ".signed.bin" not in args.firmware[0]:
        eprint("Expecting firmware to end with '.signed.bin'")
        return 1

    if args.usart is not None:
        serial_port = usart.SerialPort(args.usart)
        bootloader_device = _find_and_open_usart_bitbox(serial_port)
        transport: TransportLayer = usart.U2FUsart(serial_port)
        bootloader = Bootloader(transport, bootloader_device)
    else:
        bootloader_device, transport = _find_and_open_usb_bitbox02()
    bootloader = Bootloader(transport, bootloader_device)

    with open(args.firmware[0], "rb") as file:
        firmware = file.read()

    def progress(perc: float) -> None:
        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 args.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
def _find_and_open_usart_bitbox(serial_port: usart.SerialPort) -> devices.DeviceInfo:
    """
    Connects to a BitBoxBase bootloader over UART.
    If the BitBoxBase is currently running a firmware, it will
    be rebooted and this function will connect to the bootloader
    when it shows up.
    """
    print("Connecting to BitBox bootloader over UART.")
    bootloader_device: devices.DeviceInfo = get_bitboxbase_default_device(serial_port.port)
    # First, try to connect to the bootloader directly.
    bootloader_status = _try_usart_bootloader_connection(serial_port, bootloader_device)
    if bootloader_status == UsartBootloaderProbeResult.SUCCESS:
        return bootloader_device
    if bootloader_status == UsartBootloaderProbeResult.TIMEOUT:
        print("No reponse from BitBox. Maybe it's not connected properly?")
        sys.exit(1)

    # The bootloader wasn't valid, try to connect to the firmware instead.
    print("BitBox bootloader not available.")
    print("Trying to connect to BitBox firmware instead...")

    def _show_pairing(code: str) -> bool:
        print("(Pairing should be automatic) Pairing code:")
        print(code)
        return True

    try:
        transport = usart.U2FUsart(serial_port)
        bitbox_attempt = BitBoxBase(
            transport, bootloader_device, show_pairing_callback=_show_pairing
        )
        print("Connected. Rebooting.")
        bitbox_attempt.reboot()
    except usart.U2FUsartTimeoutError:
        pass
    finally:
        bitbox_attempt.close()
    print("Reboot completed.")

    # wait for it to reboot
    while True:
        bootloader_status = _try_usart_bootloader_connection(serial_port, bootloader_device)
        if bootloader_status == UsartBootloaderProbeResult.SUCCESS:
            return bootloader_device
        if bootloader_status == UsartBootloaderProbeResult.TIMEOUT:
            print("Waiting for the BitBox bootloader to show up...")
            sleep(1)
        else:
            print("Stuck in bitbox mode -  didn't reboot properly!")
def _try_usart_bootloader_connection(
    serial_port: usart.SerialPort, bootloader_device: devices.DeviceInfo
) -> UsartBootloaderProbeResult:
    """
    Probes the connection to a BitBoxBase bootloader
    over the specified UART port.
    """
    transport = usart.U2FUsart(serial_port)
    try:
        bootloader_attempt = Bootloader(transport, bootloader_device)
        bootloader_attempt.versions()
        success = UsartBootloaderProbeResult.SUCCESS
    except usart.U2FUsartErrorResponse as err:
        if err.error_code != usart.U2FUsartErrorResponse.ENDPOINT_UNAVAILABLE:
            raise
        success = UsartBootloaderProbeResult.NOT_AVAILABLE
    except usart.U2FUsartTimeoutError:
        success = UsartBootloaderProbeResult.TIMEOUT
    finally:
        bootloader_attempt.close()
    return success