diff --git a/cmdtr.py b/cmdtr.py index 446ea68..a04c5e7 100755 --- a/cmdtr.py +++ b/cmdtr.py @@ -12,7 +12,7 @@ from trezorlib.protobuf_json import pb2json def parse_args(commands): parser = argparse.ArgumentParser(description='Commandline tool for Trezor devices.') parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', help='Prints communication to device') - parser.add_argument('-t', '--transport', dest='transport', choices=['usb', 'serial', 'pipe', 'socket'], default='usb', help="Transport used for talking with the device") + parser.add_argument('-t', '--transport', dest='transport', choices=['usb', 'serial', 'pipe', 'socket', 'bridge'], default='usb', help="Transport used for talking with the device") parser.add_argument('-p', '--path', dest='path', default='', help="Path used by the transport (usually serial port)") # parser.add_argument('-dt', '--debuglink-transport', dest='debuglink_transport', choices=['usb', 'serial', 'pipe', 'socket'], default='usb', help="Debuglink transport") # parser.add_argument('-dp', '--debuglink-path', dest='debuglink_path', default='', help="Path used by the transport (usually serial port)") @@ -66,6 +66,10 @@ def get_transport(transport_string, path, **kwargs): if transport_string == 'socket': from trezorlib.transport_socket import SocketTransportClient return SocketTransportClient(path, **kwargs) + + if transport_string == 'bridge': + from trezorlib.transport_bridge import BridgeTransport + return BridgeTransport(path, **kwargs) if transport_string == 'fake': from trezorlib.transport_fake import FakeTransport diff --git a/tests/config.py b/tests/config.py index 3fc402f..6c5fbd0 100644 --- a/tests/config.py +++ b/tests/config.py @@ -4,13 +4,14 @@ sys.path = ['../',] + sys.path from trezorlib.transport_pipe import PipeTransport from trezorlib.transport_hid import HidTransport from trezorlib.transport_socket import SocketTransportClient +from trezorlib.transport_bridge import BridgeTransport devices = HidTransport.enumerate() if len(devices) > 0: if devices[0][1] != None: print 'Using TREZOR' - TRANSPORT = HidTransport + TRANSPORT = BridgeTransport TRANSPORT_ARGS = (devices[0],) TRANSPORT_KWARGS = {'debug_link': False} DEBUG_TRANSPORT = HidTransport diff --git a/trezorlib/protobuf_json.py b/trezorlib/protobuf_json.py index 0ba7d26..412a885 100644 --- a/trezorlib/protobuf_json.py +++ b/trezorlib/protobuf_json.py @@ -41,10 +41,10 @@ __version__='0.0.5' __author__='Paul Dovbush ' -import json # py2.6+ TODO: add support for other JSON serialization modules +import json from google.protobuf.descriptor import FieldDescriptor as FD import binascii - +import types_pb2 as types class ParseError(Exception): pass @@ -112,7 +112,7 @@ _ftype2js = { #FD.TYPE_MESSAGE: pb2json, #handled specially FD.TYPE_BYTES: lambda x: binascii.hexlify(x), FD.TYPE_UINT32: int, - FD.TYPE_ENUM: int, + FD.TYPE_ENUM: str, FD.TYPE_SFIXED32: float, FD.TYPE_SFIXED64: float, FD.TYPE_SINT32: int, @@ -132,7 +132,7 @@ _js2ftype = { # FD.TYPE_MESSAGE: json2pb, #handled specially FD.TYPE_BYTES: lambda x: binascii.unhexlify(x), FD.TYPE_UINT32: int, - FD.TYPE_ENUM: int, + FD.TYPE_ENUM: lambda x: getattr(types, x), FD.TYPE_SFIXED32: float, FD.TYPE_SFIXED64: float, FD.TYPE_SINT32: int, diff --git a/trezorlib/transport.py b/trezorlib/transport.py index afe59d4..63f0c72 100644 --- a/trezorlib/transport.py +++ b/trezorlib/transport.py @@ -19,7 +19,7 @@ class Transport(object): def _close(self): raise NotImplementedException("Not implemented") - def _write(self, msg): + def _write(self, msg, protobuf_msg): raise NotImplementedException("Not implemented") def _read(self): @@ -51,7 +51,7 @@ class Transport(object): def write(self, msg): ser = msg.SerializeToString() header = struct.pack(">HL", mapping.get_type(msg), len(ser)) - self._write("##%s%s" % (header, ser)) + self._write("##%s%s" % (header, ser), msg) def read(self): if not self.ready_to_read(): @@ -73,9 +73,12 @@ class Transport(object): def _parse_message(self, data): (msg_type, data) = data - inst = mapping.get_class(msg_type)() - inst.ParseFromString(data) - return inst + if msg_type == 'protobuf': + return data + else: + inst = mapping.get_class(msg_type)() + inst.ParseFromString(data) + return inst def _read_headers(self, read_f): # Try to read headers until some sane value are detected diff --git a/trezorlib/transport_bridge.py b/trezorlib/transport_bridge.py new file mode 100644 index 0000000..77e864e --- /dev/null +++ b/trezorlib/transport_bridge.py @@ -0,0 +1,74 @@ +'''BridgeTransport implements transport TREZOR Bridge (aka trezord).''' + +import binascii +import requests +import protobuf_json +import json +import mapping +from transport import Transport +import messages_pb2 as proto + +TREZORD_HOST = 'http://localhost:21324' +CONFIG_URL = 'https://mytrezor.com/data/plugin/config_signed.bin' + +class BridgeTransport(Transport): + def __init__(self, device, *args, **kwargs): + + r = requests.get(CONFIG_URL) + if r.status_code != 200: + raise Exception('Could not fetch config from %s' % CONFIG_URL) + + config = binascii.unhexlify(r.text) + + r = requests.post(TREZORD_HOST + '/configure', data=config) + if r.status_code != 200: + raise Exception('trezord: Could not configure') + + r = requests.get(TREZORD_HOST + '/enumerate') + if r.status_code != 200: + raise Exception('trezord: Could not enumerate devices') + enum = r.json() + + if len(enum) < 1: + raise Exception('trezord: No devices found') + + self.path = enum[0]['path'] + self.session = None + self.response = None + + super(BridgeTransport, self).__init__(device, *args, **kwargs) + + def _open(self): + r = requests.post(TREZORD_HOST + '/acquire/%s' % self.path) + if r.status_code != 200: + raise Exception('trezord: Could not acquire session') + resp = r.json() + self.session = resp['session'] + + def _close(self): + r = requests.post(TREZORD_HOST + '/release/%s' % self.session) + if r.status_code != 200: + raise Exception('trezord: Could not release session') + else: + self.session = None + + def ready_to_read(self): + return self.response != None + + def _write(self, msg, protobuf_msg): + cls = protobuf_msg.__class__.__name__ + msg = protobuf_json.pb2json(protobuf_msg) + payload = '{"type": "%s","message": %s}' % (cls, json.dumps(msg)) + r = requests.post(TREZORD_HOST + '/call/%s' % self.session, data=payload) + if r.status_code != 200: + raise Exception('trezord: Could not write message') + else: + self.response = r.json() + + def _read(self): + if self.response == None: + raise Exception('No response stored') + cls = getattr(proto, self.response['type']) + inst = cls() + pb = protobuf_json.json2pb(inst, self.response['message']) + return ('protobuf', pb) diff --git a/trezorlib/transport_fake.py b/trezorlib/transport_fake.py index a56f442..6ceb3f2 100644 --- a/trezorlib/transport_fake.py +++ b/trezorlib/transport_fake.py @@ -17,8 +17,8 @@ class FakeTransport(Transport): def ready_to_read(self): return False - def _write(self, msg): + def _write(self, msg, protobuf_msg): pass def _read(self): - raise NotImplementedException("Not implemented") \ No newline at end of file + raise NotImplementedException("Not implemented") diff --git a/trezorlib/transport_hid.py b/trezorlib/transport_hid.py index 4a3cff5..8db11a6 100644 --- a/trezorlib/transport_hid.py +++ b/trezorlib/transport_hid.py @@ -96,7 +96,7 @@ class HidTransport(Transport): def ready_to_read(self): return False - def _write(self, msg): + def _write(self, msg, protobuf_msg): msg = bytearray(msg) while len(msg): # Report ID, data padded to 63 bytes diff --git a/trezorlib/transport_pipe.py b/trezorlib/transport_pipe.py index 74b526d..6087339 100644 --- a/trezorlib/transport_pipe.py +++ b/trezorlib/transport_pipe.py @@ -1,4 +1,4 @@ -'''TransportFake implements fake wire transport over local named pipe. +'''PipeTransport implements fake wire transport over local named pipe. Use this transport for talking with trezor simulator.''' import os @@ -42,7 +42,7 @@ class PipeTransport(Transport): rlist, _, _ = select([self.read_f], [], [], 0) return len(rlist) > 0 - def _write(self, msg): + def _write(self, msg, protobuf_msg): try: self.write_f.write(msg) self.write_f.flush() diff --git a/trezorlib/transport_serial.py b/trezorlib/transport_serial.py index 8568c94..b76d86f 100644 --- a/trezorlib/transport_serial.py +++ b/trezorlib/transport_serial.py @@ -22,7 +22,7 @@ class SerialTransport(Transport): rlist, _, _ = select([self.serial], [], [], 0) return len(rlist) > 0 - def _write(self, msg): + def _write(self, msg, protobuf_msg): try: self.serial.write(msg) self.serial.flush() @@ -36,4 +36,4 @@ class SerialTransport(Transport): return (msg_type, self.serial.read(datalen)) except serial.SerialException: print "Failed to read from device" - raise \ No newline at end of file + raise diff --git a/trezorlib/transport_socket.py b/trezorlib/transport_socket.py index a89737b..d457b8f 100644 --- a/trezorlib/transport_socket.py +++ b/trezorlib/transport_socket.py @@ -29,7 +29,7 @@ class SocketTransportClient(Transport): rlist, _, _ = select([self.socket], [], [], 0) return len(rlist) > 0 - def _write(self, msg): + def _write(self, msg, protobuf_msg): self.socket.sendall(msg) def _read(self): @@ -89,7 +89,7 @@ class SocketTransport(Transport): return self.ready_to_read() return False - def _write(self, msg): + def _write(self, msg, protobuf_msg): if self.filelike: # None on disconnected client @@ -107,4 +107,4 @@ class SocketTransport(Transport): except Exception: print "Failed to read from device" self._disconnect_client() - return None \ No newline at end of file + return None