TorChat is an anonymous, cross-platform instant messenger that uses the Tor network and encrypts correspondence. This article describes the protocol used by TorChat, and the improvements made to the TorChat implementation in Python.
Introduction, description of Tor and hidden service
Tor's anonymity system, which often pops up in the media, serves to anonymously visit and create websites. Anyone can get a “domain” of the form
test3unszyhvy7um.onion and in a few seconds this site will be available for all users of the Tor network to visit. To create a domain, an RSA key is created, from the public part of which the hash sum is calculated (in the case of this domain, it is equal to test3unszyhvy7um). Tor remembers matching a domain name to a public key in the DHT. This site is called hidden service. Tor creates a TCP connection between the client and hidden service. Through such a connection, you can skip different protocols: HTTP (S), SSH, IRC, Bitcoin, and others. A description of one of these protocols, TorChat, is in the next section.
The algorithm for connecting to hidden service is not directly related to the topic of the article, its consideration deserves a separate article. So far I will note important points. The hidden service domain name cannot be selected without access to the private part of the RSA key. It is impossible to listen to the channel between the client and hidden service or replace the data on this channel. It is impossible to find the IP address where the hidden service is running or the IP address of its client.
')
TorChat protocol
This section is based on the
tc_client.py file. There are enough comments in the file, but there is no protocol in text form.
Clients interact directly through Tor, there are no servers in the TorChat system. The client starts its own Tor process or uses an already running Tor, controlling it via the Control port.
Each user has his own hidden service with a domain name like abc.onion, on which he listens to port 11009. The first part of the name (abc) has a length of 16 characters, may consist of characters 234567abcdefghijklmnopqrstuvwxyz (base32) and serves as a TorChat ID. Each user has the ability to connect to other users through their TorChat ID.
Tor guarantees that only the creator, the holder of the corresponding key, can control this domain name. However, there is no information about who connects to the hidden service, so the authentication includes the creation of a reverse connection. So let's say Alice (alice.onion) connects to Bob (bob.onion). To do this, Alice sends Bob a message like "ping alice <random string from Alice>". Bob sends Alice "ping bob <random string from Bob>" and "pong <random string from Alice>". Alice replies "pong <random string from Bob>". The parties compare sent and received random strings. Matching the lines confirms that the incoming connection is really from who it "presents". Thus, both have a pair of sockets (incoming and outgoing) and confidence that the incoming socket is from the same person the outgoing is sent to. Socket messages are transmitted only in one direction (with the exception of file transfers that are sent in the opposite direction, so as not to compete with text transmission).
TorChat protocol message scheme:
<command> <encoded> \ n
The command can contain only lowercase Latin letters and underscores. Encoded consists of any characters except the end of line character. The end of line character is replaced by “\” and “n”. Previously, "\" is replaced by "\" and "\".
List of commands:
- ping, pong - see above.
- not_implemented - sent to the sender if the recipient does not understand the command.
- client - transmits the name of the client, for example TorChat.
- version - sends the client version, for example, 0.9.9.553.
- status - passes the status.
Available
walked away
busy (xa). There are other statuses that are not transmitted:
offline (offline),
waiting for a handshake Handshake continues from the moment an outgoing connection is established and a ping is sent, but no pong is received. The client reports his status after pong, after changing the status or at least once every 120 seconds. - profile_name - passes the nickname of the user. Nick is optional. In addition to the nickname, which the user says about himself, there is a local nickname that the local user assigned. The local nick has higher priority when displayed.
- profile_text is an optional detailed description that a user can fill out about himself.
- profile_avatar_alpha - avatar's alpha channel (64 * 64 * 8 bits). Sent to the data of the avatar itself. If the avatar is not present, then this message is skipped or sent by launching a string.
- profile_avatar - avatar (64 * 64 * 24 bits). Optional. If sent, then sent after profile_avatar_alpha.
- add_me - request to add to the contact list. Customer version 0.9.9.553 agrees to add to the contact list of all comers.
- remove_me - request to exclude from the list of contacts. The client should not automatically be added back to the contact list after receiving such a message.
- message is a plain text message. Client version 0.9.9.553 rejects messages if the sender is not in the contact list.
- filename - file transfer request. It includes the transmission identifier, the size in bytes, the block size in bytes (ignored by the receiver in version 0.9.9.553) and the name of the file being transferred. Fields are separated by a space. This and other file transfer commands are sent through an incoming connection, and not through an outgoing connection, so that file transfer does not interfere with textual correspondence. To start the transfer does not require confirmation of the receiving party, both parties can cancel the active transfer. Client version 0.9.9.553 records the transfer to a temporary file, after the transfer is completed, the data is transferred to the file specified by the user.
- filedata - sends a fragment of the file. It includes the transmission identifier, the offset in bytes, the fragment md5 and the fragment itself. Sent in order.
- filedata_ok - confirmation of receipt of a fragment of the file. Includes transmission identifier and offset in bytes. Sent in order. The sender stops sending fragments if it does not receive confirmation of their receipt. In client version 0.9.9.553, the transfer is suspended after 16 unconfirmed fragments.
- filedata_error - sent instead of filedata_ok in case of a receive error (skip fragment, md5 mismatch). Includes transmission identifier and offset in bytes. The sender must resume transmission from the specified location.
- file_stop_sending - the sender transmits this command if it decides to abort the transfer. Includes a transfer ID.
- file_stop_receiving - the receiver transmits this command if it decides to abort the transfer. Includes a transfer ID.
Implementations
I managed to find 4 implementations of the TorChat client.
Python torchat_py from Prof7bit (Bernd Kreuss, Hannover, Germany), 2007. The first implementation. Now it is in the torchat_py repository on github.
torchat2 on Lazarus + Free Pascal by Prof7bit. New implementation, 2012. Simplified to run multiple instances on the same machine. The kernel is completely separate from the GUI, which allows you to run, including without a GUI. Implemented a plugin for the Purple library, which is used by IM-clients Pidgin and Finch. Uses just one execution thread per program. Python's creates multiple threads for each contact.
TorChat for Max OS X from Julien-Pierre Avérous, France. In 2010, it was written in C ++, in 2013, the code was uploaded to
github , then from C ++, they moved to Objective-C. It is possible to make private notes about the interlocutor or block the interlocutor. There is a multiplayer chat.
jTorchat in Java from daux2a. Written in 2012. File transfer is not implemented. Added a broadcast mode that allows you to send messages to all users of the TorChat network, even those who are not in the contact list. Implemented a random chat request from the network.
Representation in official distributions
The distributions of Gentoo, Debian, OpenSuse, Fedora and Windows have been studied.
At the moment TorChat is included only in the Debian distribution.
Package Page:
packages.debian.org/wheezy/torchat
Python implementation
Consider the implementation of TorChat in Python version
0.9.9.553 .
When sending messages to a recipient who is not online, these messages are stored locally and sent with the [delayed] prefix when the recipient appears on the network at the same time as the sender. The sender is notified [delayed messages have been sent].
If you leave TorChat turned on for a week and do not use it, you’ll get about 50 megabytes of outgoing and 100 incoming traffic. Creating a new account occurs instantly (RSA key generation time), the first activation takes half a minute. Subsequent activations occur in a couple of seconds. Apparently, during the first activation, time is spent on the “first acquaintance” of the Tor program with the Tor network.
When creating a new account TorChat, it is automatically added to the contacts to itself under the nickname self. This is useful for many reasons. Firstly, the status of this contact shows whether our account is online. Secondly, you can quickly copy your TorChat ID (right mouse button - Copy ID to clipboard). Third, by “chatting” with you, you can estimate the network latency. Usually ping about 1 second. Fourthly, this contact is convenient to use in plugins, for example in a conference (see below).
The portable.txt file is in the program folder. If it is, the program folder is used to store configs. Otherwise, the folder ~ / .torchat or ~ / .torchat_ <account name> is used. The account name is supplied as a command line argument. The config includes the buddy-list.txt and torchat.ini files and the Tor folder with an RSA key.
Of interest are the modules
tc_client.py (kernel),
tc_gui.py (GUI),
dlg_settings.py (settings window),
config.py (settings storage).
In the
tc_client.py file
there are classes:
- BuddyList - a list of contacts. One copy of the class for the program. It has methods for adding and removing contacts.
- Buddy - contact. May or may not be in the contact list. The method for sending a message and a little higher method that works when receiving messages.
- Classes "ProtocolMsg_ <command>", the heirs of ProtocolMsg are the managers of protocol messages. The execute (self) method is responsible for the action performed when the corresponding command is received. These classes are very convenient for plugin development.
The
tc_gui.py file
contains the wxPython code. Of the interesting classes:
ChatWindow (chat window),
MainWindow (main window, contains a link to BuddyList).
There is only one class in the
dlg_settings.py file, Dialog, which is responsible for the settings window. Adding your items to the settings window from the plugin is done by changing the
addPluginSettings method (the method is added to my fork).
The
config.py file contains the
set and
get wrappers for the ConfigParser from the standard Python library. In the same file are the values ​​of the default settings (config_defaults). Settings are stored in the file torchat.ini.
The contact list is stored in the buddy-list.txt file in a table format (torchat_id [local nickname]).
Translations are implemented as lang_xx.py files, where xx is the language code. The files are in the
translations folder. Each text for translation is stored in a variable referenced from the rest of the program. Non-standard option, but convenient when writing plug-ins: you just need to create the necessary variables in the appropriate modules. TorChat is translated into many languages, including Russian (2011, translator: SB14.org, RusInfo.cc).
Python implementation improvements
People complained that TorChat lacks some functions. Some of them, such as the ability to run multiple instances, are very simple to implement. It is strange that the author did not immediately make them. Others (for example, multi-user chats) are not so simple, but they can be implemented without changing the protocol (the method was described in Habré). Finally, there are things that require protocol changes, such as voice communication. I'm not at all sure that its implementation is possible, taking into account the delays of the Tor network. Finally, there are things that probably will not work. These things include video calling.
When a critical mass of requests to TorChat had accumulated, I began to think about how to get these functions. Unfortunately, the main developer is currently inactive, he is not in TorChat and cannot be contacted by mail. I didn’t want to write on Pascal, Java, Objective-C or Max OS X, so I relied on the old Python implementation, contrary to the author’s preferences. Python and the implementation of TorChat in Python have proven to be very convenient for writing plugins and making improvements. An additional argument was that it was the Python implementation that was included in at least one distribution kit, Debian.
When I began to study the source code, at first it seemed to me that it was not for nothing that they said that the code was of poor quality. Apparently, the description of the protocol contributed to this exclusively in the source code. However, then I looked and saw that the protocol and implementation were carried out conscientiously. I daresay that the author, when he wrote this code a few years ago, did not have much experience with Python. In some places you can meet <>, which speaks of the "Baysik" or "Pascal" past. However, the code is written to fame.
I
forked the repository on github and pretty quickly did everything I wanted. What was done:
- Plugin system . At startup, the program searches for plugins in the subfolder of plugins located in the same folder as the program file (torchat.py) and in the subfolder of plugins located in the same folder as the torchat.ini configuration file. Plugins are Python modules, that is, * .py files. The plugin must have its name in the variables NAME_xx, where xx is the language code (en, ru, etc). The plugin must have a load (torchat) function that loads the plugin. It is supplied with the torchat argument, which contains the link to the torchat module, through which you can get to the other modules (config, tc_client, tc_gui, dlg_settings). A plugin can replace classes or functions in these modules so as not to break the program or interfere with other plugins. By default, all plugins are disabled, the user turns on the ones you need in the "Plugins" tab in the settings and restarts the program.
- Multiple accounts . The SOCKS5 proxy port launched by Tor and the port on which TorChat accepts incoming connections from Tor are “sewn up” to the original program. To change them, it was necessary not only to get into the settings, but also to manually edit the torrc config. To avoid these difficulties, the default ports were changed to 0, which is interpreted as any free port. The port set in the settings is now perceived as desired, but not mandatory - if it is busy, the program connects any available port. The config for Tor with the necessary changes is generated automatically.
- Change the display name and version of the client as a plugin . The more versions of TorChat, the wider the possibilities for using this information for de-anonymization or matching. Usually, programs resist such functions, but this is still anonymous chat, not a browser, so anonymity comes first. Moreover, this is done in the form of a plugin, which is disabled by default.
- Ping companion in the form of a plugin . Sends ping and measures delay before getting pong. The delay appears in the chat window. To measure ping, an item is made in the chat context menu. For the sake of this plugin, I had to change tc_client.py, to allow pong to be sent more than once per connection. The change does not violate security and is compatible with previous versions.
- Game stone-scissors-paper in the form of a plug . Adds items to choose from the chat context menu. The game protocol is as follows: each opponent chooses a stone “r”, scissors “s” or paper “p” and random salt no shorter than 10 characters. The result of adding the lines: salt, "-" and the choice (1 letter) - is in clear text. From it sha1 is calculated and sent to the opponent. Then the plaintext is sent. I found a bash script for playing on one of OnionNet's IRC channels. I liked the idea and I wrote a Qt program for the game. In TorChat, this game also does not hurt.
- Password to add to the contact list as a plugin . In the settings you enter a password and a hint to it (optional). When you add an account to your contacts, an automatic message is sent asking you to enter a password and a hint. If the password is entered correctly, the account is added to the contact list. Until then, there are no manifestations of attempts to add to contacts. This plugin can be useful for protection from random idlers or from spam. If bots for sending spam via TorChat appear, the password “4” with the prompt “two and two” will save them.
- Conferences are implemented as a plugin . The account on which this plugin is uploaded turns into a conference server, and people from its contact list become conference members. When someone from the contact list writes something to the conference, she forwards his message to all of her online contacts, with a note from whom the message is from. The conference assigns a role to each user. The role system is quite simple, similar to the role system in Skype. The role is retained by the user when he leaves the network or leaves the conference. In addition to the main role (user), there are guests who can read what others are writing, but cannot write to the conference themselves. There is a “Nobody” role that can only receive help with the! Help command. And there is the role of “Banned”, which can not do anything. Banned users are automatically disconnected from the conference. Let's move up the role system: moderator, administrator, owner. The owner of the conference is only one, this is the conference itself, power emanates from it for administrators whom it can appoint and remove. Administrators can change different conference settings, including avatar, subject (conference nickname) and description. Administrators can assign moderators who can kick, ban, insert and remove a gag from the mouth of ordinary users and invite users to the conference. Administrators can set a default role. To make a closed conference, you need to explicitly assign all of them their role, and then change the default role to “Banned”. You can protect the conference with a password using the appropriate plug-in. By default, the “prefer_nicks” setting is enabled, which makes torchat ID of conference members invisible to conference members with a role younger than a moderator. This eliminates the conference members from spam on their torchat ID and complicates the deanonymization. (You must first find the conference, then “shake” the torchat user ID from its admin, then find it ...) Conference members can send each other private messages through the conference, their contents are not protected from the conference. Torchat ID can be exchanged via the conference “lichku” and after that you can conduct a dialogue directly via TorChat. You can ignore users with a role below the moderator, then their messages, including the PM, are not delivered to the ignorer. A conference pegi5xdl3m4re3c3 has been created for this article.
- A plugin that disables the display of messages directed to oneself. Useful in conjunction with the conference plugin so that the conference owner who writes to the conference from the conference account does not see duplicates of his messages.
- The contact list is now stored in JSON format as part of the configuration INI file. For contacts is stored not only the local alice, but also the nickname selected by the user.
- Bugfixes
I would like to include them in Debian separately from the innovations.
What's next?
- 4 execution threads are created for each contact: for the incoming connection, for the outgoing connection, and 2 for the transfer of files (reception, issue). It is quite wasteful. You could not create execution streams for file transfers until this transfer occurs. Better yet, use asyncore and generally reduce everything to a single thread for the entire program. The problem is that asyncore does not include the ability to connect through a proxy. I did not find a complete solution for this. You can highlight this work in a separate project asyncore + socks. By the way, the Pascal implementation spends only 1 thread per process.
- Dynamically loaded and unloaded plugins. And without the need to write unload () for each plugin.
- Flexible customization of Tor accounts and processes. Ability to connect to an already running torus. Ability to use different Tor processes for inbound and outbound connections. The ability to use "dual Tor" (Tor, connecting to the network through another Tor). The use of the tails in the distribution (there is no way to launch a separate Tor process, since such a process will not have access to the network by default). The ability to directly send messages (not via Tor), if the sender and the recipient are on the same machine (for example, the conference and its administrator). When changing the configuration, do not restart the Tor process, but update the configuration in the running process via the Control port or the SIGHUP signal (Linux).
- Disposal of dependence on the GUI, the ability to run in console mode or via the web interface.
- The plug-in that creates a “site” on port 80 of the hidden service, so that you could write messages to the user without installing TorChat, but directly via the Tor Browser. In conjunction with the conference plugin should get a web chat.
- File sharing plugin that allows you to upload and download files. Useful in conjunction with the conference.
- Digital signature issued to the user when adding it to contacts. If the list of contacts is lost, but the key is preserved, then at the new launch contacts will add this user. To distinguish them from spam bots, it is useful to give them a digital signature in advance confirming that they are in contact. It is also useful to encrypt the signature with your own key so that she would not prove anything to anyone except us.
- Generate key (s) from random seed. Only this random seed will be sent to the backup. Contact list copy is optional (see previous paragraph).
- Plugin for port forwarding from user to user. Not only TCP, but also UDP support is desirable. Through the forwarded port it will be possible to pass arbitrary protocols, including audio. Speaking through Tor is already possible: torfone.org
- Make changes to upstream. Open pull request . Back up the fixes to the Debian wheezy and send the new version to the Debian sid.
UPD .
Interview with Bernd Kreuss, author of the stick, August 2013.