Pull to refresh

Анонимный чат TorChat и его улучшение

Reading time 14 min
Views 77K
TorChat

TorChat — это анонимный кроссплатформенный мессенджер, использующий сеть Tor и шифрующий переписку. В данной статье рассмотрен протокол, используемый TorChat, и улучшения, внесённые в реализацию TorChat на Python.

Введение, описание Tor и hidden service


Система обеспечения анонимности Tor, которая часто всплывает в СМИ, служит для анонимного посещения и создания сайтов. Любой желающий может получить «домен» вида test3unszyhvy7um.onion и через несколько секунд этот сайт станет доступен для посещения всеми пользователями сети Tor. Для создания домена создается RSA-ключ, от публичной части которого вычисляется хеш-сумма (в случае данного домена она равна test3unszyhvy7um). Tor запоминает соответствие доменного имена публичному ключу в DHT. Такой сайт называется hidden service. Tor создает TCP-соединение между клиентом и hidden service. Через такое соединение можно пропускать разные протоколы: HTTP(S), SSH, IRC, Bitcoin и другие. Описание одного из таких протоколов, TorChat, находится в следующем разделе.

Алгоритм подключения к hidden service не связан напрямую с темой статьи, его рассмотрение заслуживает отдельной статьи. Пока что отмечу важные моменты. Доменное имя hidden service невозможно отобрать без доступа к приватной части ключа RSA. Невозможно прослушивать канал между клиентом и hidden service или подменять данные на этом канале. Невозможно узнать IP-адрес, на котором работает hidden service или IP-адрес её клиента.

Протокол TorChat


Данный раздел базируется на файле tc_client.py. В файле достаточно комментариев, однако протокол в текстовом виде отсутствует.

Клиенты взаимодействуют напрямую через Tor, серверов в системе TorChat нет. Клиент запускает собственный процесс Tor или использует уже запущенный Tor, управляя им через Control port.

Каждый пользователь имеет собственный hidden service с доменным именем вида abc.onion, на котором он слушает порт 11009. Первая часть имени (abc) имет длину 16 символов, может состоять из символов 234567abcdefghijklmnopqrstuvwxyz (base32) и служит в качестве TorChat ID. Каждый пользователь имеет возможность подключаться к другим пользователям через их TorChat ID.

Tor гарантирует, что данным доменным именем может управлять только его создатель, держатель соответствующего ключа. Однако о том, кто подключается к hidden service, нет никакой информации, поэтому аутентификация включает создание обратного соединения. Итак, допустим, Алиса (alice.onion) подключается к Бобу (bob.onion). Для этого Алиса отправляет Бобу сообщение вида «ping alice <случайная строка от Алисы>». Боб отправляет Алисе «ping bob <случайная строка от Боба>» и «pong <случайная строка от Алисы>». Алиса отвечает «pong <случайная строка от Боба>». Стороны сравнивают отправленные и полученные случайные строки. Совпадение строк подтверждает, что входящее соединение действительно от того, кем оно «представляется». Таким образом, оба имеют по паре сокетов (входящий и исходящий) и уверенность, что входящий сокет от того же, кому направлен исходящий. Сообщения по сокетам передаются только в одном направлении (за исключением передач файлов, которые передаются в противоположном направлении, чтобы не конкурировать с передачей текста).

Схема протокольного сообщения в TorChat:
<command> <encoded>\n

Команда может содержать только строчные латинские буквы и знаки подчеркивания. Encoded состоит из любых символов, кроме символа конца строки. Символ конца строки заменяется на «\» и «n». Предварительно «\» заменяется на «\» и «\».

Список команд:
  • ping, pong — см. выше.
  • not_implemented — отсылается отправителю, если получатель не понимает команды.
  • client — передаёт название клиента, например TorChat.
  • version — передаёт версию клиента, например 0.9.9.553.
  • status — передаёт статус. Доступен (available), отошёл (away), занят (xa). Есть и другие статусы, которые не передаются: нет в сети (offline), ожидание ответного соединения (handshake). Handshake продолжается с того момента, как установлено исходящее соединение и отправлен пинг, но не получен понг. Клиент сообщает свой статус после понга, после изменения статуса или хотя бы раз в 120 секунд.
  • profile_name — передаёт ник пользователя. Ник необязателен к заполнению. Кроме ника, который пользователь сам о себе сообщает, есть локальный ник, который присвоил локальный пользователь. Локальный ник имеет больший приоритет при отображении.
  • profile_text — необязательное подробное описание, которое может заполнить о себе пользователь.
  • profile_avatar_alpha — альфа-канал аватара (64*64*8 бит). Отправляется до данных самого аватара. Если аватара нет, то это сообщение пропускается или отправляется пуская строка.
  • profile_avatar — аватар (64*64*24 бит). Необязательное. Если отправляется, то отправляется после profile_avatar_alpha.
  • add_me — запрос о добавлении в список контактов. Клиент версии 0.9.9.553 соглашается добавлять в список контактов всех желающих.
  • remove_me — запрос об исключении из списка контактов. Клиент не должен автоматически добавляться обратно в список контактов после получения такого сообщения.
  • message — обычное текстовое сообщение. Клиент версии 0.9.9.553 отвергает сообщения, если отправителя нет в списке контактов.
  • filename — запрос на передачу файла. Включает идентификатор передачи, размер в байтах, размер блока в байтах (в версии 0.9.9.553 игнорируется получателем) и имя передаваемого файла. Поля разделены пробелом. Эту и остальные команды передачи файлов отправляют через входящие соединение, а не через исходящее, чтобы передача файла не мешала текстовой переписке. Для начала передачи не требуется подтверждения принимающей стороны, обе стороны могут отменить активную передачу. Клиент версии 0.9.9.553 записывает передачу во временный файл, после окончания передачи данные переносятся в файл, указанный пользователем.
  • filedata — передаёт фрагмент файла. Включает идентификатор передачи, смещение в байтах, md5 фрагмента и сам фрагмент. Отправляются по порядку.
  • filedata_ok — подтверждение получения фрагмента файла. Включает идентификатор передачи и смещение в байтах. Отправляются по порядку. Отправитель перестает отправлять фрагменты, если не получает подтверждения их получения. В клиенте версии 0.9.9.553 передача приостанавливается после 16 неподтвержденных фрагментов.
  • filedata_error — отправляется вместо filedata_ok в случае ошибки получения (пропуск фрагмента, несовпадение md5). Включает идентификатор передачи и смещение в байтах. Отправитель должен возобновить передачу с указанного места.
  • file_stop_sending — отправитель передаёт эту команду, если решает прервать передачу. Включает идентификатор передачи.
  • file_stop_receiving — получатель передаёт эту команду, если решает прервать передачу. Включает идентификатор передачи.


Реализации


Мне удалось найти 4 реализации клиента TorChat.

torchat_py на Python от Prof7bit (Bernd Kreuss, Hannover, Germany), 2007 год. Первая реализация. Сейчас находится в ветке torchat_py репозитория на github.
torchat2 на Lazarus + Free Pascal от Prof7bit. Новая реализация, 2012 год. Упрощён запуск нескольких экземпляров на одной машине. Ядро полностью отделено от GUI, что позволяет запускать в том числе без GUI. Реализован плагин для библиотеки Purple, которую используют IM-клиенты Pidgin и Finch. Использует всего один поток исполнения на программу. Питоновская создает несколько потоков на каждый контакт.
TorChat для Max OS X от Julien-Pierre Avérous, Франция. В 2010 году написан на C++, в 2013 году код залит на github, затем от C++ перешли к Objective-C. Есть возможность делать приватные заметки о собеседнике или блокировать собеседника. Есть многопользовательский чат.
jTorchat на Java от daux2a. Написан в 2012 году. Не реализована передача файлов. Добавлен широковещательный режим, позволяющий передавать сообщения всем пользователям сети TorChat, даже тем, которых нет в списке контактов. Реализован запрос случайного собеседника из сети.

Представленность в официальных дистрибутивах

Были изучены дистрибутивы Gentoo, Debian, OpenSuse, Fedora и Windows.
На данных момент TorChat включён только в дистрибутив Debian.
Страница пакета: packages.debian.org/wheezy/torchat

Реализация на Python


Рассмотрим реализацию TorChat на Python версии 0.9.9.553.

При отправке сообщений получателю, которого нет в сети, эти сообщения сохраняются локально и отправляются с префиксом [delayed], когда получатель появляется в сети одновременно с отправителем. Отправитель получает уведомление [delayed messages have been sent].

Если оставить TorChat включённым на неделю и не пользоваться им, то получится примерно 50 мегабайт исходящего и 100 входящего трафика. Создание нового аккаунта происходит мгновенно (время генерации ключа RSA), первая активация занимает полминуты. Последующие активации происходят за пару секунд. Видимо, при первой активации время тратится на «первое знакомство» программы Tor с сетью Tor.

Когда создают новый аккаунт TorChat, он автоматически добавляется в контакты к себе под ником self. Это полезно по многим причинам. Во-первых, статус этого контакта показывает, в сети ли наш аккаунт. Во-вторых, можно быстро скопировать свой TorChat ID (правая кнопка мыши — Copy ID to clipboard). В-третьих, «переписываясь» с собой, можно оценить задержку сети. Обычно пинг порядка 1 секунды. В-четвёртых, этот контакт удобно использовать в плагинах, например в конференции (см. ниже).

В папке с программой лежит файл portable.txt. Если он есть, папка программы используется для хранения конфигов. Иначе используется папка ~/.torchat или ~/.torchat_<название аккаунта>. Название аккаунта подается аргументом командной строки. Конфиг включает файлы buddy-list.txt и torchat.ini и папку Tor с RSA-ключом.

Интерес представляют модули tc_client.py (ядро), tc_gui.py (GUI), dlg_settings.py (окно настроек), config.py (хранилище настроек).

В файле tc_client.py есть классы:
  • BuddyList — список контактов. Один экземпляр класса на программу. Имеет методы для добавления и удаления контактов.
  • Buddy — контакт. Может быть или не быть в списке контактов. Метод для отправки сообщения и чуть выше метод, срабатывающий при получении сообщений.
  • Классы «ProtocolMsg_<команда>», наследники ProtocolMsg — менеджеры протокольных сообщений. Метод execute(self) отвечает за действие, выполняемое при получении соответствующей команды. Эти классы очень удобны для разработки плагинов.

В файле tc_gui.py находится код на wxPython. Из интересных классов: ChatWindow (окно чата), MainWindow (основное окно, содержит ссылку на BuddyList).

В файле dlg_settings.py всего один класс, Dialog, отвечающий за окно настроек. Добавление своих пунктов в окно настроек из плагина происходит путём подмены метода addPluginSettings (метод добавлен в моём форке).

Файл config.py содержит функции-обёртки set и get для ConfigParser из стандартной библиотеки Python. В этом же файле находятся значения настроек по умолчанию (config_defaults). Настройки хранятся в файле torchat.ini.

Список контактов хранится в файле buddy-list.txt в табличном виде (torchat_id [локальный ник]).

Переводы реализованы как файлы lang_xx.py, где xx — код языка. Файлы лежат в папке translations. Каждый текст для перевода хранится в переменной, на которую ссылаются из остальных частей программы. Нестандартный вариант, зато удобен при написании плагинов: нужно просто создать необходимые переменные в соответствующих модулях. TorChat переведён на много языков, в том числе на русский (2011 год, переводчик: SB14.org, RusInfo.cc).

Улучшения реализации на Python


Люди жаловались, что в TorChat не хватает кое-каких функций. Некоторые из них, например возможность запуска нескольких экземпляров, очень просты в реализации. Странно, что автор их сразу не сделал. Другие (например многопользовательские чаты) уже не так просты, однако их можно реализовать без изменения протокола (способ описывался на хабре). Наконец, есть вещи, требующие изменений протокола, например голосовая связь. Я вообще не уверен, что её реализация возможна, с учётом задержек сети Tor. Наконец, есть вещи, которые наверняка сделать не получится. К таким вещам относится видеосвязь.

Когда накопилась критическая масса запросов к TorChat, я начал думать, как получить эти функции. К сожалению, основной разработчик в данный момент неактивен, его нет в TorChat и по почте с ним не удаётся связаться. Писать на Pascal, Java, Objective-C или Max OS X мне не хотелось, поэтому сделал ставку на старую Python-реализацию, вопреки предпочтениям автора. Python и реализация TorChat на Python оказались очень удобными для написания плагинов и внесения улучшений. Дополнительным аргументом стало то, что именно Python-реализацию включили хотя бы в один дистрибутив, Debian.

Когда я стал изучать исходники, мне сначала показалось, что не зря говорили, что код низкого качества. Видимо, этому поспособствовало описание протокола исключительно в исходниках. Однако потом я вгляделся и увидел, что протокол и реализация выполнены на совесть. Осмелюсь предположить, что автор, когда писал этот код несколько лет назад, не имел особого опыта с Python. Кое-где можно встретить <>, которое говорит о «бейсиковском» или «паскалевском» прошлом. Тем не менее, код написан на славу.

Я форкнул репозиторий на github и довольно быстро сделал всё, что хотелось. Что было сделано:
  • Система плагинов. При запуске программа ищет плагины в подпапке plugins, расположенной в одной папке с файлом программы (torchat.py) и в подпапке plugins, расположенной в одной папке с файлом конфигурации torchat.ini. Плагины представляют собой модули Python, то есть файлы *.py. В плагине должно быть определено его название в переменных NAME_xx, где xx — код языка (en, ru, etc). В плагине должна быть функция load(torchat), выполняющая загрузку плагина. В неё подается аргумент torchat, содержащий ссылку на модуль torchat, через который можно добраться до остальных модулей (config, tc_client, tc_gui, dlg_settings). Плагин может подменять классы или функции в этих модулях так, чтобы не ломать программу и не мешать другим плагинам. По умолчанию все плагины отключены, пользователь включает нужные во вкладке «Плагины» в настройках и перезапускает программу.
  • Несколько аккаунтов. В исходную программу был «зашит» порт SOCKS5-прокси, запускаемого Tor, и порт, на котором TorChat принимает от Tor входящие соединения. Для их изменения нужно было не только залезать в настройки, но и править вручную конфиг torrc. Чтобы избежать этих сложностей, порты по умолчанию были изменены на значение 0, которое интерпретируется как любой свободный порт. Порт, выставленный в настройках, теперь воспринимается как желаемый, а не обязательный — если он занят, программа связывает любой доступный порт. Конфиг для Tor с нужными изменениями генерируется автоматически.
  • Изменение отображаемого названия и версии клиента в виде плагина. Чем больше версий TorChat, тем шире возможности по использованию этой информации для деанонимизации или сопоставления. Обычно программы сопротивляются таким функциям, но это же всё-таки анонимный чат, а не браузер, поэтому анонимность на первом месте. Тем более, это сделано в виде плагина, который по умолчанию отключён.
  • Пинг собеседника в виде плагина. Отсылает пинг и замеряет задержку перед получением понга. Задержка появляется в окне чата. Для измерения пинга сделан пункт в контекстном меню чата. Ради этого плагина пришлось изменить tc_client.py, разрешить отсылку понга более одного раза на соединение. Изменение не нарушает безопасность и совместимо с предыдущими версиями.
  • Игра камень-ножницы-бумага в виде плагина. Добавляет пункты для выбора в контекстном меню чата. Протокол игры таков: каждый соперник выбирает камень «r», ножницы «s» или бумагу «p» и случайную соль не короче 10 символов. Результат сложения строк: соль, "-" и выбор (1 буква) — является открытым текстом. От него вычисляется sha1 и отправляется сопернику. Затем отправляется открытый текст. Я нашел bash-скрипт для игры на одном из каналов IRC-сети OnionNet. Мне понравилась идея и я написал программу на Qt для игры. В TorChat эта игра тоже не помешает.
  • Пароль на добавление в список контактов в виде плагина. В настройках вводится пароль и подсказка к нему (необязательная). При добавлении аккаунта в контакты присылается автоматическое сообщение с просьбой ввести пароль и подсказкой. Если пароль введён верно, то аккаунт добавляется в список контактов. До этого не отображается никаких проявлений попытки добавиться в контакты. Данный плагин может быть полезен для защиты от случайных бездельников или от спама. Если появятся боты для рассылки спама через TorChat, то от них спасёт пароль «4» с подсказкой «дважды два».
  • Конференции реализованы в виде плагина. Аккаунт, на котором загружен этот плагин, превращается в сервер конференции, а люди из его списка контактов становятся членами конференции. Когда кто-то из списка контактов пишет что-то конференции, она пересылает его сообщение всем своим контактам, находящимся в сети, с пометкой, от кого это сообщение. Конференция присваивает роль каждому пользователю. Система ролей довольно проста, похожа на систему ролей в скайп. Роль сохраняется за пользователем, когда он выходит из сети или покидает конференцию. Помимо основной роли (user), есть гости, которые могут читать, что пишут другие, но не могут сами писать в конференцию. Есть роль «Никто», которая может только получать помощь командой !help. И есть роль «Забаненный», которая ничего не может. Забаненные пользователи автоматически отключаются от конференции. Двинемся «вверх» по системе ролей: модератор, администратор, владелец. Владелец у конференции всего один, это сама конференция, от него исходит власть для администраторов, которых он может назначать и снимать. Администраторы могут менять разные настройки конференции, включая аватар, тему (ник конференции) и описание. Администраторы могут назначать модераторов, которые могут кикать, банить, вставлять и вынимать кляп изо рта простых пользователей и приглашать пользователей в конференцию. Администраторы могут выставлять роль по умолчанию. Чтобы сделать закрытую конференцию, нужно явно закрепить за всеми их роль, после чего изменить роль по умолчанию на «Забаненный». Можно защитить конференцию паролем, посредством соответствующего плагина. По умолчанию включена настройка «prefer_nicks», которая делает torchat ID членов конференции невидимым для членов конференции с ролью младше модератора. Это позволяет избавить членов конференции от спама на их torchat ID и усложняет деанонимизацию. (Нужно сначала найти конференцию, затем «вытрясти» из её админа torchat ID пользователя, потом его найти...) Члены конференции могут слать друг другу личные сообщения через конференцию, их содержимое не предохраняется от конференции. Через «личку» конференции можно обменяться torchat ID и после этого вести диалог напрямую через TorChat. Можно игнорировать пользователей с ролью младше модератора, тогда их сообщения, включая личку, не доставляются до игнорирующего. Для данной статьи создана конференция pegi5xdl3m4re3c3.
  • Плагин, отключающий отображение сообщений, направленных самому себе. Полезен в сочетании с плагином конференции, чтобы владелец конференции, пишущий в конференцию с аккаунта конференции, не видел дубликатов своих сообщений.
  • Список контактов теперь хранится в формате JSON как часть INI-файла с конфигурацией. Для контактов хранится не только локальный алис, но и ник, выбранный пользователем.
  • Багфиксы.

    Хотелось бы включить их в Debian отдельно от новшеств.


Что дальше?


  • На каждый контакт создается 4 потока исполнения: для входящего соединения, для исходящего соединения, и 2 для передачи файлов (прием, выдача). Это довольно расточительно. Можно было бы не создавать потоки исполнения для передачи файлов, пока не происходит этой передачи. А ещё лучше использовать asyncore и вообще свести всё к одному потоку на всю программу. Проблема состоит в том, что asyncore не включает возможности подключаться через прокси. Готового решения для этого я не нашел. Можно выделить эту работу в отдельный проект asyncore+socks. Кстати, паскалевская реализация тратит всего 1 поток на процесс.
  • Динамически загружаемые и выгружаемые плагины. При этом без необходимости писать unload() для каждого плагина.
  • Гибкая настройка соответствия аккаунтов и процессов Tor. Возможность подключения к уже запущенному тору. Возможность использовать разные процессы Tor для входящих и исходящих соединений. Возможность использования «двойного Tor» (Tor, подключающийся к сети через другой Tor). Использование в дистрибутиве tails (там запуск отдельного процесса Tor не пройдёт, так как выхода в сеть у такого процесса не будет по умочанию). Возможность прямой передачи сообщений (не через Tor), если отправитель и получатель находятся на одной машине (например конференция и её администратор). При изменении конфигурации не перезапускать процесс Tor, а обновлять конфигурацию в запущенном процессе через Control port или сигнал SIGHUP (Linux).
  • Избавление от зависимости от GUI, возможность запуска в консольном режиме или через веб-интерфейс.
  • Плагин, создающий «сайт» на порту 80 hidden service, чтобы писать сообщения пользователю можно было без установки TorChat, а непосредственно через Tor Browser. В сочетании с плагином конференции должен получиться веб-чат.
  • Плагин «файлообменник», позволяющий загружать и скачивать файлы. Полезен в сочетании с конференцией.
  • Цифровая подпись, выдаваемая пользователю при добавлении его в контакты. Если потерялся список контактов, но сохранился ключ, то при новом запуске контакты добавят данного пользователя. Чтобы отличать их от спам-ботов, полезно заранее выдавать им цифровую подпись, подтверждающую, что они находятся в контактах. Полезно также зашифровать подпись своим же ключом, чтобы ни для кого, кроме нас, она ничего не доказывала.
  • Генерация ключа(ей) из random seed. В резервную копию направится только этот random seed. Список контактов копировать необязательно (см. предыдущий пункт).
  • Плагин для проброса портов от пользователю к пользователю. Желательна поддержка не только TCP, но и UDP. Через проброшенный порт можно будет пропускать произвольные протоколы, в том числе аудио. Говорить через Tor уже можно: torfone.org
  • Внести изменения в апстрим. Открыт пулл-реквест. Бекпортить фиксы в Debian wheezy и отправить новую версию в Debian sid.


UPD. Интервью с Bernd Kreuss, автором торчата, август 2013.
Tags:
Hubs:
+24
Comments 14
Comments Comments 14

Articles