Trezor Communication Inquiries

Hello Everyone, I hope you are all doing well.

I have some questions regarding Trezor Communication between laptop (Trezor Suite or Trezorctl command line) and wallet (for old firmware versions), as I understand it Trezor uses a Server-Client structure (where the wallet/device is the server and the host on which the trezor Suite or trezorctl is running is the client) with HTTP/1.1 (for old firmware versions). It also uses google protocol buffers as well as nanopb libraries to define message types and serialise data exchanged between the host and the device. This much is clear (I don’t know if my very brief explanation is correct). However, while using wireshark to monitor the packets, I got confused :

  • The syn/ syn-ack / ack handshake is initiated from port 37006 (random port changing each time) to port 21325 on localhost. If I understand it well, the port number 21325 is fixed and is the one used by trezorctl command line. So how come in the documentation, it is mentioned that the device can never initiate communications ?
  • If it’s not the case and the port used by trezor device is 21325, where is this defined in the firmware code ? Also I can’t see on the firmware code anything mentioning the usage of TCP as transmission protocol (or is it innately used by the protobuf/nanopb library ?)

Any help, clarification or reference that can help me clarify the communication would be immensely appreciated.

The main point you seem to be missing is that the communication that you are seeing are local clients talking to the Trezor Bridge, not to the Trezor device.

I recommend learning more about (a) IP networking and (b) uhhh like computer peripherals in general? Because your question is hopelessly confused as a result of missing something in one of the more basic layers.

Other than that:

  • no, Trezor device does not implement HTTP protocol, nor TCP, nor does it have a port at all, because it’s not IP-networked to the host (that is why you can’t find anything in firmware code)
  • no, the device can’t initiate communication, and this has nothing at all to do with any of your observations, so I don’t understand that part of your question

Oh, so there is a tunnel that actually formats the data before sending it to the device/host. I completely forgot about the Trezor Connect module. I will look into it, thank you.

Okay so I’ve spent the past couple days trying to understand how the data exchange between the Trezor device and, th hoest to which it is connected, works. The following explanation is what I understood from the submodules defined in the trezorlib module :

  • There are 3 ‘types’ of communication : bridge, webusb or hid (the udp one is reserved for the emulator)
  • The protocol module defines 2 protocol versions : V1 doesn’t support sessions but V2 does, also The encapsulation is different in the 2 versions.
  • The types of communication only define by which ‘interface’ we’re going to send data to our Trezor device.
    DATA PROCESSING :
    So the original (unformatted data) message goes through the protobuf module to convert it respecting to the protobuf messages’ structures that can be understood by Trezor. It then gets encapsulated depending on which version of the trezor communication protocol is used. And finally the resulting data is routed either to trezor bridge, websusb or hid interface to be sent to the device.

I have some questions still :sweat_smile:

  • First of all, is my explanation accurate ?
  • On the Trezorlib module in the protocol submodule, it is mentionned that the protovol version 1 is the one used on all trezor devices with a date 11/2018. However on the Trezor One device I am using for my tests (with firmware version 1.7.1) I can see that there are sessions, however the trezor protocol V1 doesn’t support sessions. How can I check (which files/repos should I investigate) the device’s received data processing algorithm ?
  • I suppose the Trezor Device doesn’t understand HTTP requests, therefore I suppose that the bridge interface acts both as the server and the client (is that assumption correct ?). How is the data sent to the device in case of using the bridge interface ?

Yes, and hid is very obsolete, at this point it’s only to support firmware installation on unopened Trezor Ones bought more than like four years ago

The existing Trezor devices communicate over USB vendor class, interrupt transfers. This is called webusb because the intention here is to use it through WebUSB
(which is just a javascript API, plus a fancy way of signaling to browsers that they’re allowed to talk to the device. Otherwise there isn’t anything web-specific to it).

Because not all browsers support webusb (because nobody is sure that the whole concept is at all smart), you can install Trezor Bridge, which exposes a HTTP server interface, and talks to the device over USB on the caller’s behalf.
(this also has some specific advantages, like you could talk to a device even if your user doesn’t have permissions to access the USB interface – or, theoretically, to a device connected to another machine altogether)

So trezorlib basically lets you pick whether you want to connect over raw USB, using the libusb library, or to delegate this job to an already functioning Bridge instance.

I mean, yes, but actually no.
“Protocol v1” is actually the only protocol. The v2 is a never finished draft that was originally intended for TT but we decided to drop it before releasing – with the hope that it could be finalized one day. That never happened and at this point it won’t either. A “protocol v3” is coming that will include encryption, but right now that’s the only thing we know about it.

It sounds very weird to call it “unformatted data”. It’s just a message, without a defined encoding.

Kind of, yes. There’s two separate steps hiding in the “protocol”, and that is (a) TLV encoding of the protobuf-serialized message, and (b) splitting the resulting serialization into chunks of the right size that the transport can, well, transport. Bridge doesn’t need chunking, but USB does.

(and on the way back, you have to reassemble the chunks, unwrap and deserialize the message)

This isn’t actually a great design. A better choice would be to just send type+value as the encapsulation, and leave the chunking up to the transport. When talking to Bridge, the length is implicit, so you could just send the raw data. And when it gets to the USB level, the transport can make its own choice of chunking (or, i dunno, using block transfer or something).

Different sessions. Protocol-level sessions are not supported (as you can find out in the docs).

you don’t need to. there is only one.

correct

Those terms are completely irrelevant in this whole discussion.
Yes, Bridge is a HTTP server meaning that it listens to a TCP port 21324, and we call things that listen on IP ports “servers”.
Yes, Trezor communicates in client-server manner, with the device having a “server” role and the application (e.g. trezorctl) is a “client”.
These are not the same meanings of the word “server”. Again, please look that up on your own!

The role of the Bridge is a middleman, or, well, a “bridge”.

The host application (trezorctl, Suite, …) makes a HTTP POST request to the Bridge that contains the wrapped TLV bytes of the message. The Bridge splits the message into chunks and sends those chunks over USB to Trezor. Then it waits for the response, reassembles it into the wrapped TLV message, and returns that to the host application in the POST response body.


Overall i’m wondering, what is your actual question? What problem are you trying to solve?

A post was split to a new topic: Permanent address

First of all, thank you so much for taking the time and having the patience to indulge in my questions, and thank you for the detailed explanation.

To answer your question, I don’t have a problem to solve :sweat_smile:, I am just trying to understand how the communication works in order to reproduce a minimalistic implementation of your communication protocol (I want to integrate communication for only some messages in an already built tool, in order to test some functions and measure the power consumption during the execution of said functions).

1 Like

Oh cool! In that case I would recommend going with the Bridge, which saves you the headache of doing USB communication.

1 Like

Indeed, I think I am going to go with the Bridge option because it seems like the easiest way to communicate with the devices (and the less troublesome way). Thanks again, you know for the tremendous help :star_struck:. Very good time response from your part. I appreciate that

Hello everyone, your annoying useless user is back again with useless questions (not useless to me of course :sweat_smile:). So I’ve been looking more in detail into the communication between a client and the device (trezor One).

Thanks to @matejcik, I could understand how trezor communication works in detail. Furthermore, @matejcik advised me to use bridge to communicate with my device. However, I can’t use the bridge transport to communicate with Trezor for several reasons, the most important one being : I am integrating my script, that handles communication with Trezor One, in a much larger tool which will be used to execute multiple pin verification tests on the device. Seeing as Trezor Bridge is too complex and implements all message types, my tutor asked me to develop a much lighter version (implementing only messages necessary to the pin verification test).

Therefore, I have been looking into the python-trezor module in order to reproduce/recreate a simpler form of communication using the usb-hid transport layer. I have some questions regarding this ordeal :

  • in order to communicate with trezor using the usb-hid transport layer, I need to do 3 things :
  1. Define message types and messages using the protobuf library, and compile them into python objects using the pb2py script in trezor-common/protob directory.
  2. After these messages are serialized, they have to be divided into chunks following the protocol v1 encapsulation (TLV + magic numbers + chunking into 64 bytes packets)
  3. Send these messages through the usb-hid layer.

First of all, is it possible to do this through the usb-hid handler or is it no longer supported by Trezor devices ?
Assuming it’s the case :

  • steps 1 and 2 of the previous list seem easy enough (i hope) to accomplish, however step 3 seems to be a little more complicated, any help/documentation on how to do so would be appreciated.

Well the first thing you need to do is get ahold of Trezor with firmware 1.6.3 or lower :woman_shrugging: above that, it’s all webusb.

I would advise against messing with this one.

Before I expand on that, tell me: why won’t you just leverage python-trezor to do all of the communication for you, instead of reinventing bits and pieces?

Oh okay, so I really can’t communicate using usb-hid if firmware is 1.7.0 or higher ? Is it because the ‘new’ firmwares don’t impelment the hid communication anymore ?

For 3 main reasons :

  • I am going to have to explain in detail what I need to do, so please bear with me : I want to be able to execute the pin verification function implemented in the firmware using different pin values for the user pin input and the stored pin and without the button protection (the need to click on one of T-1 buttons). I suppose this is something I have to change in the firmware of the T-1 device and has nothing to do with the communication.
  • I want to integrate it in an already built tool in order to facilitate the tests for someone who isn’t familiar with the communication protocol or the python-trezor library.
  • I (still) have no idea how to use the python-trezor library directely in python scripts :sweat_smile:

Correct. HID was swapped for webusb at 1.7.0.

I strongly recommend figuring out that part. It’s easier than doing everything you need by hand.

(it’s not really different from using any other Python library. have a look at tools/helloworld.py in the python subdir.)

So uhh you want to be able to invoke the PIN dialog on Trezor, and then, like, brute-force the PIN from host? So you would need to change the layout to not be randomized for every attempt, or just to use the host-provided input directly against the stored pin, instead of going through the matrix?

That would be a change you need to do in the firmware, yes. But when you do that, you can basically just call normal GetAddress, which triggers the PIN dialog, then send the PIN from host, and you get back either another PIN request, or the address.

Two ways to do this:

  1. implement a custom ui object to give to the TrezorClient constructor. It is something that has a get_pin method. This will get called every time the PIN is shown on screen, and should return whatever the user has actually typed (so like "123" for all bottom row buttons).
  2. maybe easier for you: construct a GetAddress by hand (see btc.py::get_address), call it via client.call_raw(), and then check the return value. if it is a PinMatrixRequest, send a PinMatrixAck (see client.py::TrezorClient::_callback_pin). You can do that in a loop.

Yeah, I am finding my way around, thank you!

Indeed, I would love to use the host-provided input directly against the stored pin and therefore by-pass the matrix if possible (just for the tests so that measuring the power consumption during pin verification function would be easier)

Okay, I will try this first. Thank you

or (on firmware side) edit the code for fsm_msgPing to take the input and run the verify PIN function on it, returning the right result

and (on host side) just repeatedly call client.ping("1234") with whatever argument you need

Excellent suggestion, easier actually. Thank you !

In short, I am just going to include the functions I need from the pyhton-trezor library and use them directly in the built tool. Furthermore, I am going to modify the firmware to test the input pin against the stored pin to test the pin verification function. Thanks.