«Open Source» и «децентрализация» – это два основных шильдика, обеспечивающих хорошее первое впечатление о проекте. Нередко популярные проекты лукавят на эти темы, либо вовсе вводят своих пользователей в заблуждение, являясь проприетарными и частично централизованными. Например, полуоткрытый Telegram и весьма заметно централизованный TOR. А что мы знаем про I2P? Предлагаю подробно разобраться в том, что обеспечивает связность и работоспособность абсолютно децентрализованной сети: Флудфилы –  своеобразные доски объявлений. Начнем с того, что внутрисетевые имена I2P никак не соотносятся с какими-либо IP-адресами, как это происходит в традиционном интернете. Следовательно, нет и маршрутизации по сетям и подсетям, которую обеспечивает концепция IP. Более того, статус сети, непосредственно связанный с анонимностью, должен на что-то опираться. Согласитесь, вопрос здесь явно не про оптимально короткие пути от точки до точки, а скорее про нечто обратное: непредсказуемые маршруты и тонну хитрой криптографии. Идентификаторы внутрисетевых ресурсов, например, адреса сайтов, не содержат в себе какой-либо информации о физическом местонахождении сервера. Привычный адрес, оканчивающийся на «.b32.i2p», – всего лишь хеш SHA256 от полного адреса, который включает в себя набор криптографических ключей. Полный адрес в связке с информацией о входящих туннелях называется лизсетом, который необходим для установления связи со скрытым сервисом. Известно, что хеш-функция необратима, т.е. из значения хеша невозможно восстановить исходные данные, от которых уникальная строка была взята. Понимание этого приводит к логическому заключению, что полученный b32-адрес необходимо разрешить примерно также, как это происходит в обычной сети, когда по доменному имени определяется IP-адрес целевого ресурса: информацию возвращает DNS-сервер в ответ на запрос пользователя по конкретному имени. В I2P вместо заранее известных DNS-серверов используются флудфилы – доски объявлений, где скрытые ресурсы публикуют информацию о себе, чтобы к ним можно было обратиться.  Существует несколько фундаментальных отличий от традиционной системы доменных имен:  В I2P нет ответственных за регистрацию (не путайте с привязкой коротких адресов «.i2p», так как они, по-простому говоря, бесплатно привязываются к уже существующему длинному адресу «.b32.i2p»); Принципиально неверно постоянно хранить лизсет в одном месте, т.к. любой из флудфилов может быть злонамеренным. Размещение информации о лизсете должно быть непредсказуемым для флудфилов, но логичным для тех, кто ее ищет; Лизсет содержит в себе не только полный адрес ресурса, состоящий из криптографических ключей, но и информацию о входящих туннелях. Все туннели существуют всего 10 минут, поэтому лизсет должен регулярно обновляться, чтобы ресурс оставался доступным извне. Логика, которая позволяет публиковаться скрытому ресурсу (destination) на непредсказуемом флудфиле (floodfill), а затем другому пользователю найти этот опубликованный лизсет на практике весьма изящна: Берется целевой адрес (например, тот, который пользователь ввел в URI-строку браузера) и сегодняшняя дата. Из блока этой информации выводится хеш SHA256. Затем осуществляется поиск флудфила в локальной базе роутера: перебираются все имеющиеся. В среднем их количество колеблется от сотни до пары тысяч, зависит от условий работы конкретного I2P-роутера. Для обращения за лизсетом используется тот, который при операции ИСКЛЮЧАЮЩЕЕ ИЛИ с блоком «целевой b32-адрес + сегодняшняя дата» даст наименьшее значение.  Если опрошенный флудфил не располагает нужным лизсетом, он возвращает три флудфила из своего списка, которые, по его мнению, являются самыми подходящими для адреса, к которому мы хотим обратиться. После опроса дополнительных флудфилов, в случае неудачи, адрес считается недоступным. По схожей логике работает публикация лизсета. Для публикации выбираются два самых подходящих флудфила. При получении нового лизсета, каждый из двух флудфилов сообщает его трем наиболее подходящим флудфилам из своей базы. На этом распространение заканчивается.  Конечная точка (destination), публикующая свой лизсет, проверят качество публикации. После отправки лизсета на флудфил ожидается ответ. Если ответа нет, лизсет отправляется на следущий флудфил. В случае успешной публикации, флудфил возвращает список своих соседей, с которыми поделился лизсетом. Происходит контрольное обращение к названным соседям. Полученные лизсеты проверяются и, если все они являются копиями того, что изначально отослал инициатор, публикация считается завершенной. Публикация лизсета происходит каждые десять минут по мере устаревания туннелей.  Флудфил – это роль, которую на себя может взять любой роутер. Естественно, что такой роутер должен быть легко доступен для внешних обращений: иметь открытый рабочий порт и белый IP-адрес. Все необходимые настройки – один параметр в конфигурационном файле: floodfill = true.  С идентификаторами конечных точек разобрались: это сущности без физических адресов, которые расположены на неизвестных I2P-роутерах. А что с адресами и идентификаторами самих роутеров, тем более, что и флудфилы по своей сути также являются роутерами? У домашнего интернет-роутера есть IP-адрес, у серверных приложений в обычном интернете – IP-адрес и порт.  У I2P-роутеров также есть адрес: это набор криптографических публичных ключей. Адреса роутеров схожи с адресами скрытых конечных точек, которые были рассмотрены выше. Адрес роутера – это набор криптографических ключей – практически то же самое, что и base64-адрес конечной точки. Для короткой записи используется хеш SHA256, который дает на выходе 32 байта. По сути, это аналог b32-адреса. Чтобы не путать адреса роутеров и адреса конечных точек, идентификаторы роутеров (router ident) записываются в кодировке base64 (первая строка на скриншоте).  Трафик между роутерами зашифрован на уровне транспортных протоколов сети: NTCP2 (крипто-аналог TCP) и SSU (крипто-аналог UDP). Это скрывает абсолютно весь трафик I2P от стороннего наблюдателя, например, домашнего интернет-провайдера. При этом транспортная криптография не завязана на ключах роутера. Ключи роутера используются опционально в зависимости от типа проходящего трафика. Углубление в тему разных слоев шифрования может вызвать когнитивное расстройство неподготовленного читателя, поэтому вернемся ближе к теме. Адрес роутера не содержит данных о подключении к нему, только публичные ключи. Также это происходит с адресами скрытых сервисов, где для обращения к ресурсу нужен лизсет с входящими туннелями. У роутера вместо лизсета RI (router info) – файл, включающий в себя полный адрес и информацию о физической доступности. Этот файл статичен, поэтому нет нужды его перезапрашивать каждые десять минут. Полученный Router Info хранится в локальной директории «netDb». RI содержит специальные флаги (Router Caps), сообщающие о пропускной способности роутера и о статусе флудфила (либо об отсутствии этого статуса). В дальнейшем новый роутер может быть использован в качестве транзитного узла при построении туннелей.  В лизсете всегда указывается только короткий идентификатор первого роутера входящего туннеля (gateway). Чтобы обратиться к этому роутеру, последний узел нашего исходящего туннеля резолвит полученный адрес узла через известный ему флудфил и в случае успеха начинает передачу информации во входящий туннель нашего адресата.  Более того, при построении туннеля аналогичным образом разрешаются идентификаторы роутера-соседа каждым участником туннеля, если в его локальном хранилище нужного Router Info не оказалось. Но это снова легкое отступление от темы флудфилов к туннелям. Под термином исследования сети понимается регулярное обращение каждого роутера к известным ему флудфилам в поисках новых роутеров. Часто этот процесс называется расширением рисунка сети или зондированием.  Суть в следущем: отправляется случайное значение, в ответ на которое флудфил отправляет три самых близких в математическом смысле Router Info. Близость вычисляется логической операцией «ИСКЛЮЧАЮЩЕЕ ИЛИ». По сути, это функция стандартного обращения к флудфилу в поисках адреса, только адрес генерируется случайный, поэтому флудфил возвращает трех соседей, к которым мы могли бы обратиться, продолжая поиск «адреса». Исследовательские обращения осуществляются через специальные туннели, которые нетребовательны к скорости передачи информации. Как правило, если на роутере с низкой пропускной способностью появляются транзитные туннели, они являются исследовательскими.  Исследование сети через туннели (а не напрямую роутер-флудфил) является мерой борьбы с атаками типа «Затмение» и «Сивилла», которые направлены на изоляцию пользователя в периметре злонамеренных флудфилов, которые намеренно могут игнорировать запросы конкретного роутера. Благодаря цепочке транзитных узлов в исследовательском туннеле флудфил не знает какой роутер на самом деле запрашивает три новых роутера для расширения своего рисунка сети. Подробно модели атаки на I2P рассмотрены в статье. Так в общих чертах выглядит полностью независимая система маршрутизации анонимной сети I2P, построенная на флудфилах. При этом флудфилы держат не десять доверенных организаций, товарищ майор или Ангела Меркель, а тысячи и тысячи добровольцев. Режим флудфила увеличивает объем потребляемых ресурсов роутера. Это нужно учитывать на очень слабых устройствах вроде одноплатных компьютеров. Или нужно было учитывать раньше… Благодаря развитию I2P в виде внедрения современной криптографии, вопрос становится столь не актуальным, что в скором будущем вовсе может быть опущен. Дело в том, что все обращения к флудфилу приходят зашифрованными его асимметричным ключом шифрования. В старой реализации это ключ ElGamal, который требует много процессорного времени. В актуальном режиме работы используется криптография на эллиптической кривой (тип шифрования ECIES_X25519_AEAD, кодовое обозначение 4). По мере обновления сети, старое ресурсоемкое шифрование остается в прошлом, так как по умолчанию используется новый тип.  Уже сегодня есть удачные примеры работы i2pd в режиме флудфила на роутерах OpenWRT. О перспективах превращения кофеварки в анонимуса поговорим в одной из следующих статей. Энтузиаст отказоустойчивых и скрытых сетей