• XSS.stack #1 – первый литературный журнал от юзеров форума

i2p

Ar3s

Старожил форума
Легенда
Регистрация
30.12.2004
Сообщения
3 357
Реакции
1 404
[mod][Ar3s:] Внимание. Это перепост![/mod]

Данная статья предназначена для тех, кто хотел бы разработать собственного I2P клиента «с нуля». Предполагается знакомство с основными концепциям и понятиями I2P. На настоящий момент на это счет имеется достаточно документации и статей, в том числе и переведённых на русский язык. С другой стороны имеется официальная документация, достаточно хорошо описывающая протоколы и форматы сообщений. К сожалению она носит разрозненный характер, при этом многие неочевидные вещи там отсуствуют. Данная статья написана в первую очередь на основе изучения и отладки официального I2P джава-клиента. Конечной целью является реализация полностью на C++. Исходный код проекта в текущем состоянии располагается на гитхабе.

Используемое шифрование

Для построения собственного I2P маршутизатора необходимо наличие реализации следующих алгоритмов шифрования:
Эль-Гамаль (ElGamal). Ассиметричное шифрование, основанное на возведении основания в степень по модулю. Основание и модуля являются фиксированными константами для всей сети I2P. Помимо блоков стандартного размера в 514 байта, также используются блоки нестандартного размера в 512 байт.
Диффи-Хельман (Diffie-Hellman) для получения общего ключа симметричного ключа шифрования путем обмена публичными ключами. Используются те же самые ключи что и для Эль-Гамаля.
DSA для создания и проверки электронной подписи
AES а двух режимах: CBC с использованием ключа шифрования и вектора инициализации (IV), ECB для шифрования собственно IV длиной 16 байт
SHA256 для вычисления хэшей
Adler32 для вычисления контрольной суммы сообщений


Основные протоколы

I2P сеть состоит из 4-х основных уровней:
Транспортный уровень. Это зашифрованные Интернет соединения TCP/IP или UDP. Включает в себя установку соединений и шифрование.
Тоннели. «Окна» узлов во внешний мир, располагающиеся на других узлах и позволяющие скрывать свое истинное местоположение. Состоят из последовательности узлов, соединенных между собой протоколами транспортного уровня. Тоннель можно упрощенно представлять себе как цепочку прокси-серверов для анонимизации как клиента так и сервера.
«Чеснок». Передача сообщений или последовательности между двумя конечными узлами произвольным маршрутом и тоннелями. Характеризуется идентификаторами сессий и асссиметричным, а, после установки сессии, симметричным шифрованием
Протоколы прикладного уровня для передачи пользовательских данных между узлами.

Каждый уровень добавляет свое собственное шифрование разного назначения. Шифрование транспортного уровня скрывает трафик от провайдера, туннелей — содержимое и направление от промежуточных узлов туннелей, «чеснок» — от конечных узлов тоннелей при передаче сообщений между тоннелями.

Транспортный уровень

Для того чтобы установить соединение транспортного уровня требуется знать IP адрес и порт. Существует список известных узлов, называемый netDb, изменяющийся в процессе работе, информация о новых узлах поступает от других узлов. Первоначально список узлов скачивается со специальных сайтов, адреса которых явно перечислены в файле router/networkdb/Reseeder.java. Протокол, работающий поверх TCP/IP называетcя NTCP, а поверх UDP — SSU. Помимо некоторых отличий в установке соединений, SSU из-за пакетной природы поддерживает разбивку длинных сообщений на несколько фрагментов. Передаваемые сообщения состоят из заголовка, I2PN сообщения (о протоколе I2NP ниже) и контрольной суммы. Периодически передается специальное сообщение, содержащее текущее время с целью синхронизации. При установке соединения происходит обмен публичными ключами маршутизаторов, на основе которых по алгоритму Диффи-Хельмана вычисляется общий ключ для AES шифрования каждый на своей стороне.

Тоннели

Тоннели всегда однонаправленные — все сообщения могут передаваться только от входного узла (Gateway) к выходному узлу (Endpoint). В зависимости от того какой конец тоннеля принадлежит его владельцу, обладающему всей полнотой информации о тоннеле, тоннели делятся на входящие (владелец — выходной узел) и исходящие (владелец — входной узел). Промежуточным узлам тоннеля неизвестно, является ли тоннель входящим или исходящим, единственное действие, осуществляемое промежуточным узлом, это шифрование сообщения своим ключом шифрования и передача следующему узлу. Отсюда вытекает важное следствие: последовательное расшифрование сообщений тоннеля должно осуществляться его владельцем, поскольку только у владельца есть ключи шифрования всех промежуточных узлов. Данный факт достаточно тривиален для входящих тоннелей, т. е. получив сообщение выходной узел должен его последовательно расшифровать, однако для исходящих тоннелей оригинальное незашифрованное сообщение должно быть последовательно расшифровано перед его отправкой. Тоннели, для которых данный узел не является владельцем, называются транзитными. Транзитные тоннели передают чужой трафик и необходимы для поддержки функционирования всей сети I2P, тем самым превращая узел в маршутизатор. Узлы тоннелей используют AES шифрование с тремя различными ключами: один используется для шифрования ответа узла при создании тоннеля, а два других для передачи данных через тоннель: один ключ шифрует сами данные, а другой шифрует вектор ипициализации (IV) для шифрования данных. При этом IV шифруется тем же самым ключом дважды: до шифрования и после, называется это двойным шифрованием (double encryption). Узел получает эти два ключа в относящейся к нему записи сообщения создания тоннеля, зашифрованной его публичным ключом с помощью Эль-Гамаля.
Внутри тоннелей передаются исключительно сообщения TunnelData, вообще говоря состоящие из нескольких фрагментов. Для передачи между тоннелями используется сообщение TunnelGateway. Хотя в официальной документации написано, что для двустороннего соединения необходимо как минимум 4 тоннеля (2 входящих и 2 исходящих), на самом деле передавать сообщения через исходящие тоннели необязательно, а можно отправить сообщение TunnelGateway входному узлу нужного входяшего тоннеля.
В сообщении TunnelData контрольная сумма вычисляется из содержательных данных, следующих за нулевым байтом и присоединённым к нему не зашифрованным IV.

Протокол I2NP

Обмен данными внутри сети I2P происходит с помощью I2NP сообщений разных типов. Каждое сообщение содержит заголовок с его типом и длиной, позволяющий определить границы между сообщениями. В зависимости от типа длина сообщения может варьироваться от 20 до 64К байт. Каждый уровень использует сообщения- «обертки», содержащие внутри себя другие I2NP сообщения более высокого уровня. Для тоннелей такими «обертками» являются сообщения TunnelData для передачи внутри тоннелей и TunnelGateway для передачи между тоннелями. Для «чеснока» — Garlic. Большую часть I2P трафика представляют собой следующий вложенные сообщения:
Data->Garlic->TunnelData.
Как правило сообщения передаются через тоннели, хотя могут передаваться и напрямую между маршутизаторами, в частности для первоначального создания новых тоннелей. Также маршутизаторы обмениваются сообщениями DatabaseStore сразу после установки соединения. Сообщения между точками назначения следует передавать через «чеснок», поскольку соответствующее поле присутствует только там.

Маршутизаторы и точки назначения (destinations)

Для работы в сети I2P необходим I2P клиент, состоящий из маршутизатора, обеспечивающего доступ в сеть I2P, и точек назначения для обмена содержательной информацией. Информация о маршутизаторах в том числе и об их IP адресах является общедоступной, более того, актуальный список маршутизаторов можно скачать со специальных ftp-сайтов. В то же время информация о местоположении точек назначения является конфиденциальной. Информацией о точках назаначения, расположенном на данном маршутизаторе, располагает только данный маршутизатор, для все остальных получение этой информации не представляется возможным, что является одним из основных механизмов обеспечения анонимности сети I2P.
Поскольку маршутизаторы в основном располагаются на компьютерах участников сети, то их состав все время изменяется. Поэтому маршутизаторы вынужденны постоянно поддерживать свой список другим маршутизаторов в актуальном состоянии. Этот процесс называется «зондированием» (exploratory), заключающийся в посылке запросов со случайно выбранным 32-байтным адресом специальным маршутизаторам, называемых floodfill. Предполагается что floodfill-маршутизаторы обладают всей полнотой информации о сети. Помимо все прочего floodfill-маршутизаторы постоянно сообщают друг другу информацию о найденных новых узлах.
Для запроса информации об узле используются I2NP сообщение DatabaseLookup, а для передачи самой ифмации DatabaseStore. Как правило сообщения передаются через тоннели, однако DatabaseStore передается узлом напрямую на траспортном уровне сразу после установки соединения, тем сам сообщая сети о своем существовании. В противном случае построение тоннелей для новых узлов было бы невозможно.
DatabaseStore может содержать информацию двух видов, в случае если данному адресу соотвествуют структура RouterInfo, то адрес является маршутизатором, а если LeaseSet то точкой назначения.
RouterInfo содержит публичные ключи маршутизатора, а также разнообразную служебную информацию, наиболее важной из которой являются IP адреса, порты и поддерживаемые транспортные протоколы для соединения и сведения о том является ли даныый маршутизатор floodfill-ом или нет. Поскольку RouterInfo может содержать довольно много текстовой информации, то передается заархивированным gzip-ом.
LeaseSet, содержит список входящих туннелей данной точки назначения, а также публичный ключ для шифрования «чесночных» сообщений, предназначенной данной точке назначения.

Службы прикладного уровня

Рассмотрим содержательные действия I2P клиента: анонимный хостинг онлайн ресурсов, и, соответственно, доступ к ним. Для начала попробуем получить данные с некоторого веб-сайта, например, Флибусты. На данный момент у нас имеется только 32-байтный хэш ее I2P адреса, нашей целью же является отправка HTTP-запроса, и получение ответа.
Разумеется маршутизатор с таким адресом в базе отсутствует (иначе IP адрес ресурса был бы виден всем), поэтому единственный способ отправить запрос — это какой-нибудь входящий тоннель нужного узла, существующий в данный момент времени, для чего сначала следует запросить и получить LeaseSet. В отличие от RouterInfo, который можно запросить и получить с соседнего узла на транспортном уровне, LeaseSet можно запросить и получить только через туннели, которые предварительно нужно построить. Отсюда следует неутешительный вывод, что использовать I2P сеть «по требованию» не получится, I2P маршутизатор должен быть запущен и должен постоянно заниматься построением и поддержкой тоннелей. Из-за децентрализованности сети построение тоннелей весьма непростое дело — большинство попыток создания тоннелей оканчиваются неуспехом.
Для успешного построения тоннеля необходимо два условия:
Все участвующие в тоннеле узлы должны быть доступны на транспортном уровне по крайней мере предыдущему узлу в тоннеле
Все участвующие в тоннеле узлы должны быть согласны построить новый тоннель. Узел может отказать в создании тоннеля, например, в силу его загруженности

Максимальное время жизни тоннеля 10 минут, тоннель может прекратить свое существование и досрочно, если участвующий в тоннеле узел ушел в оффлайн. Поэтому владельцы тоннелей постоянно посылают тестовые сообщения чтобы поддерживать список «живых» тоннелей в актуальном состоянии.
Итак, тоннели имеются в наличии и необходимый LeaseSet имеется в наличии. Теперь можно отправить HTTP запрос и он даже достигнет адресата, однако нам желательно также и получить ответ. Для этого мы в нашем сообщение должны указать наш собственный LeaseSet, тогда ответ будет отправлен нам через какой нибудь входящий туннель и скорее всего благополучно достигнут нашего узла. Поскольку через наш узел может одновременно работать несколько соединений, то каждому из них должен быть либо назначен собственный I2P адрес и сформирован LeaseSet из нескольких входящих туннелей, либо создан «разделяемый» адрес, мультиплексирующий соединения, используя специальный протокол соответствующими полями, являющийся «оберткой» над протоколом прикладного уровня. Такой протокол называется I2CP и в официальном I2P клиента используется исколючительно он, хотя для построения собственных служб это необязательно. Разумеется для доступа к Флибусте следует использовать I2CP, посколько она ожидает именно его. Однако для построения к примеру собственной торрентоподобной сети можно обойтись только I2P адресацией.

Добавлено в [time]1387304248[/time]
В предыдущей статье были рассмотрены задачи, необходимые для построения I2P маршутизатора, способного принимать участие в работе сети, включающие взаимодействие с другими маршутизаторами через обычный Интернет, построение тоннелей разных видов и сбор информации о других узлах сети. Несмотря на важность этих задач, I2P клиент, выполняющий лишь функции маршутизатора, с точки зрения пользователя является «вещью в себе», поскольку не делает ничего интересного пользователю. Данная статья посвящена протоколам прикладного уровня, предназначенных для передачи пользовательских данных через сеть I2P.


Если вопросы работы I2P маршутизатора более или менее логично освящены в официальной документации, то прикладные протоколы представляют собой мешанину различных идей, сводящихся к тому, что каждый волен реализовать собственный протокол для собственного приложения, используя точки назначения в качестве адресов. Однако это нисколько не приближает к реализации собственного клиента, поскольку существующие ресурсы сети уже используют какие то протоколы и всякая новая реализация должна уметь с ними работать. В качестве примера можно посмотреть описание «чеснока», из данной страницы реально лишь понять, как следует упаковывать «чесночины» и как шифровать. Что передается между Алисой и Бобом, а также откуда Боб знает, что ответ следует отправлять именно Алисе, совершенно непонятно. Там же содержится утверждение, что с целью подтверждения доставки в одной из «чесночин» передается сообщение DeliveryStatus с указанием маршутизатора-отправителя в инструкциях для доставки, тем самым раскрывая маршутизатор, на котором сидит отправитель. Разумеется это не так. К сожалению, единственным способом узнать, как обстоят дела на самом деле, был анализ трафика, генерируемого официальным джава-клиентом.

«Чесночная» передача данных

Разберемся в этом вопросе более подробно, несколько видоизменив оригинальный пример и сделав его более практическим. Предположим что Вася Пупкин обращается к сайту Флибусты, который отныне доступен только по I2P, в то же время у Васи есть собственный сайт, оскорбляющий чьи-нибудь чувства, потому и находящийся в I2P. Для этого у Васи запущен маршутизатор, постоянно поддерживающий как минимум один исходящий и один входящий тоннели. Для своего сайта Вася создал отдельную точку назначения и опубликовал везде ее адрес. Для обращения к Флибусте ее адрес Вася уже знает, и его маршутизатор знает ее LeaseSet и может отправить туда сообщение, проблема заключается лишь в том что Васе нужно получить ответ от Флибусты, а для этого ей нужно знать адрес Васи. С этой целью на васином маршутизаторе создается еще одна точка назначения, служащая в качестве обратного адреса для все соединений, инициируемых Васей. Не только для Флибусты, но и всех остальных сайтов. При необходимости можно создать и несколько таких обратных адресов, но тогда также придется строить LeaseSet-ы с разными непересекающимися наборам тоннелей.

e18c6d9bfcb687d471156a40ea3c3f96.jpg


Для передачи данных между точками назначения используется I2NP сообщение Garlic — «чеснок». Сами сообщения передаются и шифруются между маршутизаторами, используя для этого отдельную пару ключей шифрования, публичный ключ которой передается в LeaseSet-е. Этот ключ не совпадает с публичным ключом маршутизатора, и в отличие от последнего, генерируется по новой при каждом старте. Внутри сообщение состоит из «чесночин», каждая из которых состоит из I2NP сообщения и инструкций для его доставки (delivery instructions) 4-х видов:
Локальный. Сообщение предназначено самому маршутизатору. Как правило чей-то LeaseSet.
Точка назначения. Сообщение предназначено точке назначения, подключенной к маршутизатора. Является единственным способом отправки данных точке назначения.
Тоннель. Сообщение предназначено для отправки в указанный входящий тоннель, начинающийся на указанном маршутизаторе. Используется для подтверждения доставки «чеснока».
Маршутизатор. Сообщение предназначено для отправки другому маршутизатору. Черезвычайно опасно с точки зрения безопасности, если указанный маршутизатор отличен от собственного или пользующегося доверием. На практике не встречалось.

Как правило используются «чесноки», состоящие из двух «чесночин». Первой является I2NP сообщение DeliveryStatus с номером сообщения самого «чеснока» и доставкой в один из входяших тоннелей маршутизатора отправителя. Используется для подтверждения доставки всего «чеснока» по назначению. Второй является I2NP сообщение Data, содержащее сами передаваемые данные с доставкой в точку назначения. Иногда присутствует и третья «чесночина» — LeaseSet точки назначения (не маршутизатора) отправителя. В нашем примере это LeaseSet обратного адреса Васи. Такая «чесночина» присутствует в двух случаях: в самом первом сообщении и при изменении LeaseSet-а. Делается это для того чтобы маршутизатор знал каким образом отослать ответ отправителю, иначе пришлось бы запрашивать соответствующий LeaseSet у floodfill маршутизаторов, который скорее всего там будет отсутствовать, как, например, обратный адрес Васи, а LeaseSet секретного сайта Васи наоборот там будет присутствовать.
Возникает вопрос, откуда Флибуста знает, что ответ следует отправить именно Васе, а не Петей или еще кому нибудь из обращающихся к ней в тот же самый момент. Даже если бы было известно, что «чеснок» пришел от маршутизатора Васи, чего, конечно же, не имеет место быть, то это бы помогло не сильно, поскольку на маршутизаторе Васи помимо обратного адреса имеется также его сайт и, возможно, еще много чего. Оказывается, данная информация должна содержаться внутри передаваемых в сообщении Data данных, что свидетельствует о неудачном дизайне всей системы, поскольку не позволяет достичь изоляции протоколов разных уровней друг от друга. Иначе говоря, адрес точки назначения должен присутствовать одновременно как в самих данных так и в протоколе для передачи этих данных.

Данные протокола I2CP

Изначально протокол I2CP разрабатывался исключительно для обмена между различными приложениями и маршутизатором — его сообщения в саму сеть I2P попадать не должны. Однако содержимое сообщений SendMessageMessage и MessagePayloadMessage передается по сети внутри I2NP сообщений Data, и представляют собой архивированные gzip-ом данные со специальным образом измененным заголовком вида.

0x1F 0x8B 0x08 — префикс gzip-а
1 байт флагов gzip-а
2 байта TCP или UDP порт отправителя
2 байта TCP или UDP порт получателя
1 байт дополнительных флагов gzip-а
1 байт типа протокола: 6 — потоковый (streaming), 17 — дейтаграммный (datagram), 18 — «сырой» (raw)

Таким образом каждое сообщение Data, переданное через «чеснок» всегда будет начинаться с 0x1F 0x8B 0x08 и первым делом распаковывается gzip и, в зависимости от типа протокола, обрабатывается соответствующей реализацией.

Потоковый протокол (streaming)

Потоковый протокол аналогичен TCP протоколу и гарантирует последовательность передачи данных. Сообщения состоят из заголовка и собственно данных. Тип сообщения определяется полем флагов, аналогично TCP имеются флаги SYN/FIN для установки и разрыва соединения. В отличие от TCP также могут присутствовать вплоть до 255 NACK-ов — номеров пропущенных сообщений с требованием переслать их повторно, что более характерно для пакетных протоколов и требует более сложной реализации. Также содержатся стандартные для TCP поля: 4-х байтные порты отправителя и получателя, называемые потоками, номера последовательности и номера подверждения.
При установке соединения происходит обмен сообщениями с установленным флагом SYNCHRONIZE, при этом обязательно должны быть установлены флаги FROM_INCLUDED и SIGNATURE_INCLUDED. Первый означает что в заголовке присутствует полный 387-байтный I2P адрес, а второй, что все сообщение подписано закрытым ключом I2P адреса отправителя. Таким образом стороны узнают I2P адреса друг друга, а проверка подписи гарантирует, что эти адреса настоящие. Иначе говоря, Вася, подключаясь к адресу Флибусты, после установки соединения может быть уверен, что это именно Флибуста, а Флибуста узнает обратный адрес Васи.
Таким образом, интерфейс потоковых протоколов может быть реализован в виде обычных сокетов, что позволяет использование I2P в сетевых приложениях с минимальными изменениями.

Источник: http://m.habrahabr.ru/post/206160/
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх