diff --git a/python/src/trezorlib/cli/debug.py b/python/src/trezorlib/cli/debug.py index 379cd7243..f1e9eb04b 100644 --- a/python/src/trezorlib/cli/debug.py +++ b/python/src/trezorlib/cli/debug.py @@ -16,7 +16,7 @@ import click -from .. import debuglink +from .. import debuglink, mapping, messages, protobuf from ..messages import DebugLinkShowTextStyle as S @@ -71,3 +71,48 @@ def show_text(connect, icon, color, header, body): return debuglink.show_text( connect(), header, body_text, icon=icon, icon_color=color ) + + +@cli.command() +@click.argument("message_name_or_type") +@click.argument("hex_data") +@click.pass_obj +def send_bytes(connect, message_name_or_type, hex_data): + """Send raw bytes to Trezor. + + Message type and message data must be specified separately, due to how message + chunking works on the transport level. Message length is calculated and sent + automatically, and it is currently impossible to explicitly specify invalid length. + + MESSAGE_NAME_OR_TYPE can either be a number, or a name from the MessageType enum, + in which case the value of that enum is used. + """ + if message_name_or_type.isdigit(): + message_type = int(message_name_or_type) + else: + message_type = getattr(messages.MessageType, message_name_or_type) + + if not isinstance(message_type, int): + raise click.ClickException("Invalid message type.") + + try: + message_data = bytes.fromhex(hex_data) + except Exception as e: + raise click.ClickException("Invalid hex data.") from e + + transport = connect(return_transport=True) + transport.begin_session() + transport.write(message_type, message_data) + + response_type, response_data = transport.read() + transport.end_session() + + click.echo("Response type: {}".format(response_type)) + click.echo("Response data: {}".format(response_data.hex())) + + try: + msg = mapping.decode(response_type, response_data) + click.echo("Parsed message:") + click.echo(protobuf.format_message(msg)) + except Exception as e: + click.echo("Could not parse response: {}".format(e)) diff --git a/python/src/trezorlib/cli/trezorctl.py b/python/src/trezorlib/cli/trezorctl.py index caa13ded5..3596e742f 100755 --- a/python/src/trezorlib/cli/trezorctl.py +++ b/python/src/trezorlib/cli/trezorctl.py @@ -157,19 +157,21 @@ def cli(ctx, path, verbose, is_json, passphrase_on_host, session_id): except ValueError: raise click.ClickException("Not a valid session id: {}".format(session_id)) - def get_device(): + def get_device(return_transport=False): try: - device = get_transport(path, prefix_search=False) + transport = get_transport(path, prefix_search=False) except Exception: try: - device = get_transport(path, prefix_search=True) + transport = get_transport(path, prefix_search=True) except Exception: click.echo("Failed to find a Trezor device.") if path is not None: click.echo("Using path: {}".format(path)) sys.exit(1) + if return_transport: + return transport return TrezorClient( - transport=device, + transport=transport, ui=ui.ClickUI(passphrase_on_host=passphrase_on_host), session_id=session_id, )