Поднимаем собственный узел в анонимной сети Hidden Lake

df143e933bd1e624c78d7852baceebf8.png

Введение

Хоть анонимные сети и являются определённого рода остатком, придатком куда большего множества всеразличных коммуникаций, тем не менее их можно рассматривать как некого рода островки приватности, абстрагированные от всего остального мира и завораживающие своей технической ухищрённостью и извращённостью. Таких островков безусловно существует множество и каждый уникален по своему. Одни анонимные сети пытаются брать количеством и расширяться на множество стран и государств, порождая тем самым множество связей между узлами. Другие анонимные сети напротив берут своим качеством, и благодаря этому им становится безразличен уровень централизации и замкнутость пространства в котором они существуют.

Анонимная сеть Hidden Lake является как раз примером последних. На её составляющих мы попробуем разобрать как можно поднимать собственные узлы. В результате, мы запустим сервис, который будет работать в сети Hidden Lake.

Для чего это нужно?

79bb2629b134e7fca67c25d687b7976b.png

Можно задаться вполне логичным и корректным вопросом -, а зачем нам всё это нужно? С какой целью? И таковых целей на самом деле может быть несколько.

  1. Самая основная, ради которой собственно и пишутся все анонимные сети — это безопасность и обезличивание транспортируемой информации. В анонимной сети Hidden Lake, помимо теоретически доказуемой анонимности, скрывающей истинную коммуникацию абонентов от всех видов пассивных атак, включая глобального наблюдателя, также реализовано и сквозное шифрование, защищающее саму информацию от третьих лиц.

  2. Анонимные сети можно использовать в качестве платформ, на которых могут быть размещены собственные ресурсы — социальные сети, мессенджеры, файловые хранилища, электронная почта, форумы, боты и т.д. Так например, в сети Tor мы вполне корректно можем на своём компьютере развернуть сайт в псевдодоменной зоне .onion, который будет иметь уникальное название и работать без необходимости: 1) аренды сервера с белым IP, или 2) покупки доменного имени, или 3) открытия портов на маршрутизаторе на принятие данных. Анонимная сеть Hidden Lake работает по схожему сценарию.

  3. Децентрализованный характер анонимной сети Hidden Lake приводит к возможности создания собственных локальных или локализированных анонимных сетей на уровне школы, университета, работы, дома, улицы, региона и т.п. Иными словами, необязательно подключаться к уже заданным узлам / ретрансляторам, чтобы иметь возможность пользоваться сетью. Данный пункт хоть и не является самодостаточным, но вполне успешно может коррелировать с первым или вторым пунктами.

  4. Увеличенное количество участников в сети может улучшить фактическую итоговую анонимность, даже если добавившийся участник просто будет бездействовать. Связано это в первую очередь с тем, что каждый участник, даже если он ничего не делает, будет постоянно генерировать ложную (пустую) информацию. Это в свою очередь будет приводить к неоднозначности связи между истинными отправителем и получателем, если таковые присоединятся к сети. Иными словами, целью здесь является само волонтёрство для поддержания первого пункта.

Почему Hidden Lake?

cda8227ebc3e3f3b088a75cbb6acec81.png

Анонимные сети являются уникальным экземпляром того, что может неявным образом порождать сама централизация. Таковые сети всегда бурно развиваются лишь в условиях надвигающейся монополизации связей, и чем сильнее таковая централизация становится, тем сильнее становятся и выдвигаемые модели угроз со стороны анонимизации трафика. Финальной формой централизации стала в конечном итоге её монолитность, выражающаяся монополизацией основных средств коммуникации. И к такой форме анонимные сети должны уметь адаптироваться.

Одним из основных направлений такой адаптации становятся сети с теоретически доказуемой анонимностью, суть которых сводится к невозможности совершения любых пассивных нападений, включая глобального наблюдателя, с целью дальнейшей деанонимизации связей или состояний. Подобные сети базируются на определённых проблемах, задачах за счёт которых они и становятся теоретически доказуемыми. На текущий момент существует такие три задачи:

  1. Проблема обедающих криптографов (DC-сети),

  2. Задача на базе очередей (QB-сети),

  3. Задача на базе увеличения энтропии (EI-сети).

Анонимная сеть Hidden Lake (HL) относится к типу QB-сетей, то есть функционирует в условиях задачи на базе очередей. Уникальной особенностью данной сети является её микросервисная архитектура, где каждый сервис может выступать в роли составного конструкта для придания общему механизму его монолитности. Сеть является одноранговой (P2P) со связью коммуникаций — friend-to-friend (F2F). Это значит, что связь между участниками является доверенной и каждый участник должен заранее внести в белый список своих друзей, с которыми он будет общаться.

05f9a3c9e20a0338dcb6dd044087e566.png

Хоть анонимная сеть и написана на языке Go, тем не менее, благодаря сервису HLS, такая сеть может работать со сторонними сервисами, написанными на других языках программирования. Основным условием связывания становится лишь HTTP-протокол, по которому HLS будет перенаправлять открытый трафик от сервиса и к сервису.

Пишем собственный сервис

Как говорилось ранее, чтобы иметь возможность запустить свой сервис в анонимной сети Hidden Lake, необходима лишь и только поддержка HTTP-протокола. Сам сервис может быть написан совершенно на любом языке программирования, будь то Python, PHP, Java, Rust и т.д. Поэтому давайте попробуем написать сервис не на языке Go (родном языке для Hidden Lake), а выберем для этого допустим язык Python.

from http.server import BaseHTTPRequestHandler, HTTPServer

class SimpleService(BaseHTTPRequestHandler):
    def _set_success_response(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def _set_failed_response(self):
        self.send_response(400)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def do_GET(self):
        self._set_success_response()
        response = "[GET] Path = " + str(self.path)
        self.wfile.write(response.encode('utf-8'))

    def do_POST(self):
        try:
            content_length = int(self.headers['Content-Length']) 
        except:
            self._set_failed_response()
            return 

        post_data = self.rfile.read(content_length)

        self._set_success_response()
        response = "[POST] Upper = " + str(post_data.decode('utf-8')).upper()
        self.wfile.write(response.encode('utf-8'))

def main():
    listener = HTTPServer(('127.0.0.1', 8080), SimpleService)
    print("Service is running...")
    try:
        listener.serve_forever()
    except KeyboardInterrupt:
        print("Service is stopped...")
    listener.server_close()

if __name__ == '__main__':
    main()

Написанный сервер работает достаточно простым образом. Если приходит запрос с методом GET, то отправляется в качестве ответа путь запроса. Если приходит запрос с методом POST, то берётся строка из тела запроса, её символы переводятся в заглавные и результат отправляется в качестве ответа.

$ curl -X GET 127.0.0.1:8080                                                                               7 ✘ 
[GET] Path = /                                                                                                                                                 
$ curl -X POST 127.0.0.1:8080 --data "hello"                                                                 ✔ 
[POST] Upper = HELLO

Поднимаем узел

Перед тем как заняться поднятием узла, нам необходимо этот узел как-то скачать и установить. Первое, что нужно сделать — это скачать репозиторий go-peer. Далее, для того чтобы установить узел существует несколько разных способов:

  1. Узел можно скомпилировать из исходников, а далее его просто запустить. Для этого необходимо установить компилятор языка Go с версией >= 1.16 и перейти в директорию cmd/hidden_lake/service. После нужно прописать команду make. Если команды make нет, то можно скомпилировать вручную: go build ./cmd/hls.

  2. Узел можно запустить в докере. Для этого необходимо иметь уже установленный docker. Переходим в директорию cmd/hidden_lake/service и запускаем make docker-default. Запустить Dockerfile можно конечно и вручную, используя команды docker build и docker run.

  3. Узел можно запустить из уже скомпилированной версии с релиза. Для упрощения и ускорения разворачивания сервисов в Linux были также написаны sh скрипты — install_hls.sh, restart_hls.sh, status_hls.sh, stop_hls.sh, которые находятся в директории cmd/hidden_lake/service/_daemon. Основной здесь скрипт — это install_hls.sh, он устанавливает последний релиз и заносит его в автозапуск.

Dockerfile

FROM ubuntu:20.04

RUN apt-get update && apt-get install -y wget gcc
RUN wget https://dl.google.com/go/go1.16.linux-amd64.tar.gz && \ 
    tar -C /opt -xzf go1.16.linux-amd64.tar.gz

WORKDIR /go-peer
ENV PATH="${PATH}:/opt/go/bin"
COPY ./ ./
RUN go build -o hls ./cmd/hidden_lake/service/cmd/hls

ENV SERVICE_PATH="/mounted"
ENV SERVICE_KEY=""
CMD ./hls -path="${SERVICE_PATH}" -key="${SERVICE_KEY}"

После того как вы запустите узел, вы увидите примерно следующую картину.

a46af2270fd70d06d655d5c89dcef1e7.png

Это говорит о том, что узел был успешно запущен. Он уже начал генерировать мусорный трафик type=BRDCS размером в 8KiB каждые 5 секунд, но пока что он ни к кому ещё не подключен. Для того чтобы его связать с другой сетью нам необходимо будет его немного настроить.

Настраиваем узел

После первого запуска узла (то есть программы HLS) будет создан один файл — hls.cfg и одна директория — hls.db. Файл hls.cfg является основным файлом настройки узла о котором мы сейчас и будем говорить. Директория hls.db исполняет лишь одну узконаправленную роль — сохраняет хеши ранее принятых или отправленных пакетов. Создано это с целью того, чтобы нельзя было дублировать пакеты даже через продолжительное время.

Если перейти в файл hls.cfg сразу после первого запуска, то мы увидим следующую картину.

{
	"settings": {
		"message_size_bytes": 8192,
		"work_size_bits": 20,
		"queue_period_ms": 5000,
		"key_size_bits": 4096,
		"limit_void_size_bytes": 4096
	},
	"logging": [
		"info",
		"warn",
		"erro"
	],
	"address": {
		"tcp": "127.0.0.1:9571",
		"http": "127.0.0.1:9572"
	},
	"services": {
		"go-peer/hidden-lake-messenger": "127.0.0.1:9592"
	}
}

Здесь существует несколько блоков. Первый блок — это настройки. Здесь говорится о том, каков должен быть размер генерируемого сообщения (message_size_bytes), какая работа должна осуществляться для генерации одного пакета (work_size_bits) [алгоритм Proof-of-Work], с каким интервалом должны генерироваться сообщения (queue_period_ms), какая длина асимметричного ключа должна быть (key_size_bits) и каков размер ложной/добавочной информации [скрывающей размер статичного сообщения в message_size_bytes] должен быть (limit_void_size_bytes). Все эти настройки прямо влияют на кооперацию с другими сетями. Если один из этих параметров начнёт отличаться в коммуникации с другими узлами, то будут происходить либо разрывы связей, либо связь будет просто-напросто нестабильна. Поэтому здесь важен консенсус выбираемых настроек. Мы оставляем блок настроенным по умолчанию.

431fd8909d9c5be705f439394f841f22.png

Далее идёт блок с логгированием информации. Такой блок выводит информацию info и warn в stdout, а erro в stderr. Сам блок может отсутствовать, либо могут отсутствовать конкретные его элементы. В таком случае удалённая часть не будет печатать логи.

Следующий блок — это блок с прослушивающими адресами. Существует всего три возможных поля: tcp, http, pprof. Первый адрес — это адрес отвечающий за подключение к анонимизирующей сети извне. Иными словами, сторонние узлы смогут подключиться к данному узлу и пропускать через него свой трафик. Второй адрес — это адрес отвечающий за действия узла через REST API. Данный адрес в идеальной обстановке должен быть локальным. Третий адрес — это фактически пакет pprof для профилирования узла. Он необходим для проверки того насколько корректно работает узел. То есть он применяется в основном для дебага и по умолчанию отключен.

Следующий блок — это блок с сервисами, к которым подключается узел. Данный блок мы как раз и будем использовать для подключения нашего написанного ранее сервиса. По умолчанию в нём хранится один сервис — go-peer/hidden-lake-messenger, который представляет одно из прикладных приложений в сети Hidden Lake, а именно мессенджер. Его также можно запустить вместе с HLS. Более подробно об этом можно почитать тут.

Помимо всех вышеописанных блоков также существуют ещё не менее важные блоки, которые по умолчанию отсутствуют, а именно — friends, network_key и connections. О friends мы поговорим немного попозже, а пока будем настраивать оставшиеся. Они связаны непосредственно с подключениями к другим сетям Hidden Lake. Мы можем создать и свои сети, но они должны будут как-то уметь обходить NAT для успешной коммуникации в глобальной сети. По умолчанию проект Hidden Lake предоставляет открытый список ретрансляторов к которым можно подключиться и через которых можно будет транслировать все сообщения. Т.к. Hidden Lake является теоретически доказуемой сетью, то ретрансляторы всё равно не будут знать о вашем состоянии в сети (общаетесь ли вы с кем-то или просто бездействуете). С таким списком ретрансляторов можно ознакомиться на странице проекта. На текущий момент список выглядит следующим образом (со временем он может видоизменяться).

621e89a334f6f4e31ac14713ac6964ca.png

И теперь можно попробовать обновить список. Можно подключиться к сети с network_key = 8Jkl93Mdk93md1bz и сразу подключиться к двум узлам с провайдеров: vdsina и ruvds. Итого, обновлённый конфиг будет выглядеть следующим образом.

{
	"settings": {
		"message_size_bytes": 8192,
		"work_size_bits": 20,
		"queue_period_ms": 5000,
		"key_size_bits": 4096,
		"limit_void_size_bytes": 4096
	},
	"logging": [
		"info",
		"warn",
		"erro"
	],
	"services": {
		"my-custom-service": "127.0.0.1:8080"
	},
	"network_key": "8Jkl93Mdk93md1bz",
	"connections": [
		"94.103.91.81:9581",
		"195.133.1.126:9581"
	]
}

Здесь я также убрал блок address и заменил сервис go-peer/hidden-lake-messenger на my-custom-service. Блок address был излишен, потому как к нам никто не будет подключаться по tcp, а также мы не будем использовать API для обращения к функциям HLS, поэтому нам не нужен http. Замена сервиса была осуществлена с условием того, что go-peer/hidden-lake-messenger в нашем примере применяться не будет.

Для того чтобы применились изменения конфига нам необходимо перезапустить узел. Если мы сейчас вновь запустим узел, то увидим уже немного изменённую картину логов.

600553a9853f3c8d19082d48553bd866.png

Здесь мы видим, что кто-то генерирует трафик, проходящий через оба ретранслятора. Такие сообщения часто будут иметь type=UNDEC, что означает невозможность расшифрования полученного пакета. Они также имеют пустые адреса, потому как само зашифрованное сообщение не разглашает отправителя. Отправителя может узнать лишь истинный получатель сообщения.

Генерируем идентификатор

При каждом новом запуске и перезапуске HLS, узел будет иметь разный приватный ключ. Иными словами, если мы свяжемся единожды с таким узлом по его публичному ключу, будем с ним общаться определённое время, а далее он перезапустится, то с ним по старому идентификатору уже будет невозможно связаться. Чтобы такого не происходило, необходимо явно указать узлу где хранится приватный ключ. Из него он уже создаст публичный и положит его в качестве своего идентификатора.

f462f1fa8fab209280d71c708e42079b.png

Положить приватный ключ в HLS можно двумя способами:

  1. При старте узла можно явно указать путь, где хранится файл с приватным ключом. Для этого необходимо прописать следующую команду: $ hls -key=priv.key . Если priv.key расположен рядом с программой hls, то приватный ключ успешно загрузится. Главное условие здесь так это то, чтобы длина ключа priv.key была равна блоку настроек key_size_bits. Иначе будет ошибка.

  2. В уже запущенный узел можно передать приватный ключ. Для этого необходимо воспользоваться API, а конкретно POST запросом на http://localhost:9572/api/node/key. Более подробно об этом можно почитать здесь.

Остаётся здесь лишь вопрос того — как именно получить этот приватный ключ. Для этого нам необходимо воспользоваться программой cmd/tools/keygen. Данную программу также можно скомпилировать вручную, либо скачать с релиза. Запускаем и главное помнить, чтобы длина сгенерированного ключа совпадала со значением в key_size_bits.

$ keygen 4096

После выполнения будет создано два файла: priv.key и pub.key. Первый как раз можно использовать на вход в HLS. Второй будет являться идентификатором в сети по которому друзья смогу отправлять сообщения.

Сгенерированные ключи

Приватный ключ. Ни в коем случае не используйте его. Он уже как видите разглашён.

PrivKey(go-peer/rsa){308209290201000282020100E23F5A321F1E6CB77DD89BA52E06E6E810DC3672CAB16571DEA47CDFF96AC41B11384A0909699C220FE50D49EC482C46C7E30F47DA705F13E4A4AF37D1954CAA91CF8368B5B5E88382FF554ADEFEB9C9FE4C899C1E9A714A849B55DF56F851F131AEBC9CF02AF357D9E3B99FD2E6AFF7D9000E03199E53F76E87F0DBE1C596B8E31B343E04823AB9EAAF022DE06D7CB293802DB30E1F655258011263AF0BA57C27BFF5AD0913FA05AEFF08725C32B876709F259823A734938B6B9419E79FA0259B27604D0E03EF20595DDB70DC97E8377C2F7E55D76363130309DE1FB5CBAC4DCC136729327D026C77DA7A0AD3A00CE7D2BA3F66ECF27A1CD76ADE8938BB1D73000BBCAD045EA3593D47383A13F83A800546666BF98A1855548F99C35E0A0FA4E2BEBBCFB4218F6BD19D7C9E822E7ADC326C797F2C6302FAA5739DB981E4AE15D104111FAFA8FF51853BE3EBE2C2A60C146F273D96D3A5928E56B5559D8AE56D52645BEE95DE4F987182A8741C66D6AF2598ED94A3417293CD09845CECCD438EA7B7E3E66FC71BB2AEFFE53457F6F5A5B443F49EABFA569759075A8AAB5BD58240528D2A20AD99FEA614424DDC58336BD693D7EEA35ACB7663E96FB4C2E316374844AB095CB6F7442081085CED353F95C08BE44D98BCAE19C486594857412765D58DBB05F4AD904BDB9459BA8BC3A0045B98342EC1C840242C726C6E551A450502030100010282020100A801575F04D0B8F681130A715C7E6F95407FE646D3D16B7D65514C119DCAF4990CEE79BBC02B966F8CD2104E2691699F9DEB347BACEFE90A82ACF018491FCEF0929A4000CF7EA78C91083E33FCE7B6DB12658F82490F72B55D47643B17000B094A55716630CA142DD6754E702A5277B55A84B9E2FF711CF50E35105C9788FC658AEFB6999F0B7DC4754B441901210C8A2711F5DD92CD29963E177DAA5C600FA0E47B9D59C2F5FCA19FD36E584DB2B45BEF419ECF1D1A9AE63D8FC34881417DD0AF284C50ABE9ABF69FA61CBFF06F35578E764E203C73672CC502DCD93B4C6544D8184870C53723479CB0B613B8168FBECE32D3C6008A06B7C81769ED081D9ABD57166999ADAAE029C24D82E5E47888F7009B546768A894B5F0EA8BD9B3688C69F2E36B58359C02904F7A3F8C476D8B84D5A92CB5AF4D6D890419058A40D2944D72EF400F88B95E8A69CAABDC524F2555231ADD7239BEAA025A85AA2C8D7A8E2B6F3786FCEEBAB7C87E60337842F253A54D7229967B8D3DEFFCCF808662104C5E6D9C089B1B302BE32827D83ED9485B80C2D5320C9CBFD8064B8049952EA1E29EF240A838B0B877273D9D35BF5BA479B8A3A01B4CDBC20C21EA6B8291F590721F90E605D944CDDB90C258890678DB38039D8E436BF364082B84D124A451E38324FD3F3080FDB446B24F3C04B31BB1705CC69B33ABC2ED148C13386FF2F0C96BB90282010100FD2DFA835984D3BCA5880E4E98F6AF6AB6738C6D32A0F280E250831A7174D0BB3228AF055056DD50C0D964B5D6FBDCB569EC569FA0C797FECD6DAA805C5FD4178FC9204E0945A4BB95730846C79900D9BE21962460D33FD9A2B35006257708A9AB83C26A0006B91DC5EA48862323428E0DC6BA7EE8588EEDD863948DCBC7428A854AC93D151B3EC3ACE91978121BE49DAB05151C3418FBF5C243B3E2CD17B635963128BC1E6F741C7DF474315CBA39694963B5D1B0ECC7A4F1D66EC579EC29F4800B3FCBF797E1F7AF92AF85895A384DCFAF37809BF2EDA3B53B6BFE50441F4DD8C397070FC33927F93391F8BFDB2BED4447AB65624AF91A59E93166315FC7CF0282010100E4C4917BA8B3A1DD48342061767D8B0B285E67389A88D58B3152186A5F3C766ADB22983BD9B7917555333D3663F68A26506822F6A0AF497E7B303EC3FF2AA988E3D599A3EEF4804DB3289E8104ECBB7D0D82A40A5ED945AE75C90E6076385997A2F2B468089D3CC34B3624D26843C3CF9D752E40A35594CA09A671FB73177F630AB06764BA923D4E83C86A219F05D439F72E8A531994B79CC32DF7A84703D84266FD4DC837FF5711B77CC24063467553E141443F658D8BC6F97FE5EB9CD2A6316836F14F08E6D10C3115AD3BA9D4906CA3A96C6F1B6745190FE8F0A5E988068B65BB8A08F0BBE66307A3FF52466DDB06057B6CFC627BBBD540716A86855106EB0282010100C9229AEF78D1C6690B5490912E82640FB20D1015BC7012D8037395040FF88ECEF8E54B4E3C6A15FECE86A6B4B3AB79C701F48948DDB1537650FB7BC5E81D1560F69101421DA99F5A8B3B4CEAF62CFE78FDCCAEFC0D9426189F623703FB059AF338583FBE7870DEE5E152E3F4F4BB51678B5842D7097D3967894E6B394F17E92C2502A9E705FA941D5E23FD60DF7A1B608AFA49B0134F79F3DF366C15957BEDFC9A6A498351E69766FF45927D98BE355808BED7365489777DF9C6B24FDE31998F2D9E423BA76299596DB96A09FB799A5B6ED03396CFCDE9AEF20ED817BCEAB1B7E5AA55347DF84953116E3FDC4F872E599BB2185F3DFC73F3D454273399AC0F0F028201002259213EDFE598B810CF42CD5F6D1A2362BC49E345D0A5BF70785312259A7E6D2CD9D4E1A79ED84835B2ABCE2ED42589FB28A7974029116FDC2F323D5B629C71E14380FE0C32C9D0E0C58886F6A654C041F70B8A43137C52E57C7A9C2123F4CFBFB7913ECD8C4E434457ACCFAAB801FE7A35CDEC069A22ECF2E5FAD73640A0EFCDFB0E8B213833DC460D21624AEE251F4EEDBDF71C302821716CC5728EB71511634545187ACFEBD2CD29FE408CA75796708BCC2378ABF800B8156C00245BBC784E9D713DDCA84B5AF943B7EEED60ED0E62C45D739139C036331D568492BBC9BFC4569433237212F56B804425A64D78453F2D78FFAC09C7FA0CE2FA8B9818C2610282010070EC7D2949ED771AFBD24174E531F913C37D108759A61BD1F3C11105C52683532D5384B972B031222805951F0F50572B2150EEFE1B2F4559805C83B3B83B89CADA3BD64FCA2782B8A6A4BE5E066355062AEC1EB553DEE683195882D472111A73E04BF9C93E057F3AA5192B0CB6AD4B0254D5FE194A206E9A49FF2F787BDDEA06D3FA797C7CD90C82AABB8A1122D6C0235275F0FD73E78904C48CB976DB6D71ED9CFECA976C5B291A65AC2B1715B81358544569DC6030CA9B1BC96E1D565D668E1251685250E464DD2F9BA186968F80792874617DBA9E92853F4190BFF3D0D7667E7D51AE7C2222CFA66C578825A12F913A61FA73AA850DCA9AFE87CF2494515D}

Публичный ключ.

PubKey(go-peer/rsa){3082020A0282020100E23F5A321F1E6CB77DD89BA52E06E6E810DC3672CAB16571DEA47CDFF96AC41B11384A0909699C220FE50D49EC482C46C7E30F47DA705F13E4A4AF37D1954CAA91CF8368B5B5E88382FF554ADEFEB9C9FE4C899C1E9A714A849B55DF56F851F131AEBC9CF02AF357D9E3B99FD2E6AFF7D9000E03199E53F76E87F0DBE1C596B8E31B343E04823AB9EAAF022DE06D7CB293802DB30E1F655258011263AF0BA57C27BFF5AD0913FA05AEFF08725C32B876709F259823A734938B6B9419E79FA0259B27604D0E03EF20595DDB70DC97E8377C2F7E55D76363130309DE1FB5CBAC4DCC136729327D026C77DA7A0AD3A00CE7D2BA3F66ECF27A1CD76ADE8938BB1D73000BBCAD045EA3593D47383A13F83A800546666BF98A1855548F99C35E0A0FA4E2BEBBCFB4218F6BD19D7C9E822E7ADC326C797F2C6302FAA5739DB981E4AE15D104111FAFA8FF51853BE3EBE2C2A60C146F273D96D3A5928E56B5559D8AE56D52645BEE95DE4F987182A8741C66D6AF2598ED94A3417293CD09845CECCD438EA7B7E3E66FC71BB2AEFFE53457F6F5A5B443F49EABFA569759075A8AAB5BD58240528D2A20AD99FEA614424DDC58336BD693D7EEA35ACB7663E96FB4C2E316374844AB095CB6F7442081085CED353F95C08BE44D98BCAE19C486594857412765D58DBB05F4AD904BDB9459BA8BC3A0045B98342EC1C840242C726C6E551A45050203010001}

Связываем друзей

После того как был настроен конфигурационный файл hls.cfg и после того как был сгенерирован приватный ключ priv.key, всё что нам остаётся — это связать друзей между собой. Анонимная сеть Hidden Lake является F2F сетью, а потому ранее созданный и настроенный узел должен иметь понимание с кем он может общаться. Он должен обладать определённым публичным ключом. Для этих целей у меня уже есть рабочий идентификатор.

PubKey(go-peer/rsa){3082020A0282020100EE8889DD0AD78CFC7F10BCE9F032B00EEA10217468BC87D821EB8CC999F763D53D2E9AF6373966C77F0DE56E9722DE6AE6D86722F6CD4E9164258BA6570B6135E181FE5C111DB63A33BD0ED406F4D3DCEB65E7ED1DF5FD1B8B3C524CF0010F43755C2AB0DF9EC4176CCFE4503BC25B08BD7791C1FED19FD50C74921CC1823114DC96DFA7FE3A2F53F70D69E7A4A629D78F83D38F6F10082550EBBE3E5EB16CA5924B7D7738876F2E00AA363B67EF655E6C468EAD5F17CFE438C1C02F873A4D9CEDC13E2886FCEDE4F828F56CE3B201A7ACB9A1D16BA93002BCAFF70E36C57826F705437C0EEE0479D470F1F4BC8E427204B5EA2CBB0BCEC78D57C448AD9D47E1B457886A50A122D9C1EF235639337CA64F4B76D3BF5E92D8F3E741B31ED5FEFD3B8E4876413CAEC96D2B78B7BC2C389B5E2C13292AC597F73C312A47FA7D6F633E309886879E1CEF11EF32FE2790D790EC370D63CAA41FBA9B915A839B0A0B2890D3C3C18701C96BA087763E2473E83EE919C17FB14AFAC3C06301CACE66CEF783D6DD3142D9F1ABDE4179F6D454D3211E68F04FBC01F4916A0ABF13804194B7EAB7C9C40A8C26A33EAC1F7B2D1499996AFB03FCC3FD5A5DA6B69167E75FD1A29CA5BC5186450A97AE8836EBF1A10E1363995F1262653D2D76164F761FE8178B9CE4B1F4154CC5852657C508622F4CA8DF2D09AB066166574FC50367645A817D0203010001}

Данный публичный ключ нам необходимо поместить в конфигурационный файл hls.cfg в блок friends. Данный блок представляет собой словарь, где ключом является никнейм (alias) участника сети, а значением является его публичный ключ. Изменённый файл будет выглядеть уже следующим образом.

{
	"settings": {
		"message_size_bytes": 8192,
		"work_size_bits": 20,
		"queue_period_ms": 5000,
		"key_size_bits": 4096,
		"limit_void_size_bytes": 4096
	},
	"logging": [
		"info",
		"warn",
		"erro"
	],
	"services": {
		"my-custom-service": "127.0.0.1:8080"
	},
	"network_key": "8Jkl93Mdk93md1bz",
	"connections": [
		"94.103.91.81:9581",
		"195.133.1.126:9581"
	],
	"friends": {
		"number571": "PubKey(go-peer/rsa){3082020A0282020100EE8889DD0AD78CFC7F10BCE9F032B00EEA10217468BC87D821EB8CC999F763D53D2E9AF6373966C77F0DE56E9722DE6AE6D86722F6CD4E9164258BA6570B6135E181FE5C111DB63A33BD0ED406F4D3DCEB65E7ED1DF5FD1B8B3C524CF0010F43755C2AB0DF9EC4176CCFE4503BC25B08BD7791C1FED19FD50C74921CC1823114DC96DFA7FE3A2F53F70D69E7A4A629D78F83D38F6F10082550EBBE3E5EB16CA5924B7D7738876F2E00AA363B67EF655E6C468EAD5F17CFE438C1C02F873A4D9CEDC13E2886FCEDE4F828F56CE3B201A7ACB9A1D16BA93002BCAFF70E36C57826F705437C0EEE0479D470F1F4BC8E427204B5EA2CBB0BCEC78D57C448AD9D47E1B457886A50A122D9C1EF235639337CA64F4B76D3BF5E92D8F3E741B31ED5FEFD3B8E4876413CAEC96D2B78B7BC2C389B5E2C13292AC597F73C312A47FA7D6F633E309886879E1CEF11EF32FE2790D790EC370D63CAA41FBA9B915A839B0A0B2890D3C3C18701C96BA087763E2473E83EE919C17FB14AFAC3C06301CACE66CEF783D6DD3142D9F1ABDE4179F6D454D3211E68F04FBC01F4916A0ABF13804194B7EAB7C9C40A8C26A33EAC1F7B2D1499996AFB03FCC3FD5A5DA6B69167E75FD1A29CA5BC5186450A97AE8836EBF1A10E1363995F1262653D2D76164F761FE8178B9CE4B1F4154CC5852657C508622F4CA8DF2D09AB066166574FC50367645A817D0203010001}"
	}
}

Перезапускаем наш узел и на этом моменте с ним мы завершаем. Он работает, работает также и наш питоновский сервис на адресе 127.0.0.1:8080. Всё, что нам осталось сделать — так это послать запрос к узлу в сети. Достучаться обычными запросами из открытого Интернета до узла не получиться. Нам необходимо использовать точно также узел, который бы отправлял запросы с идентификатора number571. Отправитель также должен обладать публичным ключом (то есть идентификатором) нашего узла. Таким образом, конфигурация отправителя может выглядеть следующим образом.

{
	"settings": {
		"message_size_bytes": 8192,
		"work_size_bits": 20,
		"queue_period_ms": 5000,
		"key_size_bits": 4096,
		"limit_void_size_bytes": 4096
	},
	"logging": [
		"info",
		"warn",
		"erro"
	],
    "address": {
        "http": "127.0.0.1:9572"
    },
	"network_key": "8Jkl93Mdk93md1bz",
	"connections": [
		"94.103.91.81:9581",
		"195.133.1.126:9581"
	],
	"friends": {
		"some-node-with-service": "PubKey(go-peer/rsa){3082020A0282020100E23F5A321F1E6CB77DD89BA52E06E6E810DC3672CAB16571DEA47CDFF96AC41B11384A0909699C220FE50D49EC482C46C7E30F47DA705F13E4A4AF37D1954CAA91CF8368B5B5E88382FF554ADEFEB9C9FE4C899C1E9A714A849B55DF56F851F131AEBC9CF02AF357D9E3B99FD2E6AFF7D9000E03199E53F76E87F0DBE1C596B8E31B343E04823AB9EAAF022DE06D7CB293802DB30E1F655258011263AF0BA57C27BFF5AD0913FA05AEFF08725C32B876709F259823A734938B6B9419E79FA0259B27604D0E03EF20595DDB70DC97E8377C2F7E55D76363130309DE1FB5CBAC4DCC136729327D026C77DA7A0AD3A00CE7D2BA3F66ECF27A1CD76ADE8938BB1D73000BBCAD045EA3593D47383A13F83A800546666BF98A1855548F99C35E0A0FA4E2BEBBCFB4218F6BD19D7C9E822E7ADC326C797F2C6302FAA5739DB981E4AE15D104111FAFA8FF51853BE3EBE2C2A60C146F273D96D3A5928E56B5559D8AE56D52645BEE95DE4F987182A8741C66D6AF2598ED94A3417293CD09845CECCD438EA7B7E3E66FC71BB2AEFFE53457F6F5A5B443F49EABFA569759075A8AAB5BD58240528D2A20AD99FEA614424DDC58336BD693D7EEA35ACB7663E96FB4C2E316374844AB095CB6F7442081085CED353F95C08BE44D98BCAE19C486594857412765D58DBB05F4AD904BDB9459BA8BC3A0045B98342EC1C840242C726C6E551A45050203010001}"
	}
}

Отличием здесь является лишь существующий блок addresses с полем http, потому как нам нужно будет генерировать самостоятельно запросы, удалённый блок services, потому что их у нас нет, а также изменённый друг с ранее сгенерированным публичным ключом.

b324362d7c40227284d435e79b02489a.png

Для запроса напишем небольшой bash скрипт. API HLS требует JSON-формата для отправления данных по сети. Поля в этом формате схожи с HTTP запросом. Основное отличие здесь — это body, которое кодируется в base64. Также заметим, что host был изменён. Теперь это не 127.0.0.1:8080, а именно my-custom-service.

#!/bin/bash

JSON_DATA='{
        "method":"POST",
        "host":"my-custom-service",
        "path":"/",
        "head":{
                "Accept": "text/html"
        },
        "body":"aGVsbG8sIHdvcmxkIQ=="
}'; # base64(hello, world!) = aGVsbG8sIHdvcmxkIQ==

JSON_DATA=${JSON_DATA//\"/\\\"} # "method" -> \"method\", ...
JSON_DATA=${JSON_DATA//[$'\t\r\n ']} # delete \t \r \n ' ' from string

PUSH_FORMAT='{
        "receiver":"some-node-with-service",
        "req_data":"'$JSON_DATA'"
}';

d="$(date +%s)";
curl -i -X POST -H 'Accept: application/json' http://localhost:9572/api/network/request --data "${PUSH_FORMAT}";
echo && echo "Request took $(($(date +%s)-d)) seconds";

Далее, в JSON_DATA все кавычки " заменяются на \", для корректной упаковки строки. После чего удаляются из строки все символы пробелов, табуляций, переноса каретки, новой строки. Получившийся результат JSON_DATA помещаем в поле req_data. Получателем назначаем узел с никнеймом = some-node-with-service. Далее, если мы запустим этот скрипт, то получим следующее сообщение.

HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 05 Oct 2023 05:16:47 GMT
Content-Length: 136

{"code":200,"head":{"Content-Type":"text/html","Server":"BaseHTTP/0.6 Python/3.11.5"},"body":"W1BPU1RdIFVwcGVyID0gSEVMTE8sIFdPUkxEIQ=="}
Request took 7 seconds

Получаем валидный ответ с кодом 200 и тело ответа в base64. Всё что остаётся — это его декодировать.

$ echo "W1BPU1RdIFVwcGVyID0gSEVMTE8sIFdPUkxEIQ==" | base64 -d                                                         1 ✘ 
[POST] Upper = HELLO, WORLD!

Можно теперь немного изменить скрипт. Уберём body, изменим path на /example, и method на GET. Получится теперь следующий скрипт. Запускаем и получаем результат.

...
JSON_DATA='{
        "method":"GET",
        "host":"my-custom-service",
        "path":"/example",
        "head":{
                "Accept": "application/json"
        }
}';
...
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 05 Oct 2023 05:27:17 GMT
Content-Length: 124

{"code":200,"head":{"Content-Type":"text/html","Server":"BaseHTTP/0.6 Python/3.11.5"},"body":"W0dFVF0gUGF0aCA9IC9leGFtcGxl"}
Request took 6 seconds
$ echo "W0dFVF0gUGF0aCA9IC9leGFtcGxl" | base64 -d                                                                       ✔ 
[GET] Path = /example

Заключение

f4f9d7340837265bc3c108f6c08153e3.png

В результате всего вышеописанного мы смогли успешно поднять узел в анонимной сети Hidden Lake, а также запустить на нём работающий сервис. Хоть ядро анонимной сети и было написано на языке Go, тем не менее, оно к нему не привязано, а это значит, что вполне корректно можно писать собственные сервисы в анонимной сети на любой вами выбранной и любимой технологии.

Литература

  1. Общая теория анонимных коммуникаций. Второе издание (Г. Коваленко). [Это буквально сборник статей, их можно найти полностью тут в открытом доступе]

  2. Системы для анонимных коммуникаций (Д. Дэйнезис, К. Диас, П. Сайверсон) [Хорошо структуризированный материал по анонимности с терминологией, примерами]

  3. Прикладная криптография. Протоколы, алгоритмы и исходные коды на языке C (Б. Шнайер). [Присутствует раздел с обзором / разбором проблемы обедающих криптографов — Широковещательная передача анонимных сообщений, глава 6.3]

© Habrahabr.ru