mirror of https://github.com/voidlizard/hbs2
350 lines
15 KiB
Plaintext
350 lines
15 KiB
Plaintext
\input texinfo
|
||
|
||
@documentencoding UTF-8
|
||
@node Top
|
||
@top Дизайн шифрования протокола
|
||
|
||
@chapter Предпосылки
|
||
|
||
@section Борьба с DPI
|
||
|
||
С высокой вероятностью, на текущем нашем уровне понимания
|
||
проблемы, DPI будут распознавать протокол всё равно.
|
||
|
||
Соответственно, нет большой разницы, один - два или ноль
|
||
незашифрованных пакетов пройдёт.
|
||
|
||
Само наличие шифрования --- уже признак для DPI, могут
|
||
блокировать просто на основании принадлежности неизвестному
|
||
протоколу.
|
||
|
||
Размеры пакетов и характер обмена --- тоже признаки.
|
||
|
||
Saltine, возможно, оставляет сигнатуры в зашифрованных пакетах
|
||
(проверить).
|
||
|
||
Таким образом, борьба с DPI совершенно точно должна
|
||
осуществляться отдельным Messaging, может быть даже внешним.
|
||
|
||
Нашего текущего уровня понимания проблемы просто-таки
|
||
недостаточно для эффективной борьбы без радикального усложнения
|
||
себе жизни.
|
||
|
||
В дальнейшем, для протокола борьбы с DPI можно выделить даже
|
||
отдельные порты, где будут ожидаться другие протоколы.
|
||
|
||
Скорее всего, нужно будет просто написать транспорт поверх
|
||
WS/WSS, а внутренние датаграммы шифровать как обычно.
|
||
|
||
Таким образом, наверху будет TLS, а внизу --- наше шифрование
|
||
пакетов, при этом протокол будет принадлежать к "хорошо
|
||
известным протоколам". Можно даже палёные сертификаты
|
||
использовать, что бы успокоить службы (на верхнем уровне).
|
||
|
||
|
||
@section Различение шифрованных и нешифрованных пакетов
|
||
|
||
Возможны следующие способы:
|
||
|
||
@itemize @bullet
|
||
@item Номера портов
|
||
@item Сам протокол
|
||
@end itemize
|
||
|
||
@subsection Номера портов
|
||
|
||
Запускаем Messaging на отдельном порту, там воркер,
|
||
который занимается шифрованием и хэндшейком,
|
||
по мере расшифровки --- передает данные в указанный Messaging
|
||
(Proxy).
|
||
|
||
|
||
Плюсы:
|
||
|
||
@itemize @bullet
|
||
@item Не нужно заглядывать в пакеты: будет быстрее.
|
||
@item Не нужны префиксы в протоколе
|
||
@item Не будет интерференции с остальными протоколами
|
||
@item Обратная совместимость
|
||
@end itemize
|
||
|
||
|
||
Минусы:
|
||
|
||
@itemize @bullet
|
||
@item Доработка PEX
|
||
@item Доработка бутстрапа
|
||
@item Различение шифрованных и нешифрованных протоколов либо
|
||
хардкода портов
|
||
@item Устойчивые порты => легко блокировать
|
||
@end itemize
|
||
|
||
@subsection Сам протокол
|
||
|
||
Поскольку Messaing работает @strong{ДО}, мы можем в нём
|
||
распаковывать пакет, доставать сообщение и передавать в Peer уже
|
||
расшифрованное.
|
||
|
||
У нас есть два варианта:
|
||
|
||
Сообщение зашифрованное и незашифрованное.
|
||
|
||
Допустим, сообщение зашифрованное --- тогда это просто пакет в
|
||
формате, который создает libsodium.
|
||
|
||
Незашифрованное --- содержит префикс, например 0xdeadf1od и
|
||
дальше --- уже сам пакет.
|
||
|
||
При приеме сообщения мы отправляем этот префикс в начале пакета.
|
||
|
||
Если префикс есть --- то смотрим, что это за пакет.
|
||
|
||
Допустим, это хэндшейк. Тогда производим согласование ключей, и
|
||
дальше шлём зашифрованные пакеты.
|
||
|
||
Допустим, префикс будет только у пакетов хэндшейка, тогда
|
||
мы даже сохраним обратную совместимость --- неразобранные пакеты
|
||
будем отправлять по стеку дальше.
|
||
|
||
Минус устойчивой сигнатуры в определенном месте --- легко
|
||
блокировать простейшим фильтром.
|
||
|
||
Несмотря на то, что наши возможности бороться с DPI ограничены
|
||
слабым погружением в вопрос, настолько облегчать работу им мы не
|
||
хотим.
|
||
|
||
Можно в префикс встроить некий байткод, выполнение которого
|
||
приведёт к вычислению публичного ключа пира, которым можно,
|
||
в частности, идентифицировать самого пира.
|
||
|
||
Тогда это не будет создавать значительных проблем самому
|
||
пиру, но будет создавать проблемы при массовом анализе
|
||
протоколов. Можно еще и загадку встроить.
|
||
|
||
Тогда сам пакет будет выглядеть более или менее как мусор,
|
||
для анализа придётся построить интерпретатор, а так же встроить
|
||
интерпретацию в DPI, успехов им в борьбе.
|
||
|
||
Можно так же использовать какой-то вид вывода ключей из хорошо
|
||
известной, но динамически меняющейся информации.
|
||
|
||
А можно использовать всё вместе, расширяя этот байткод по мере
|
||
необходимости.
|
||
|
||
Всё это приведёт к большому усложнению и сторонних реализаций
|
||
протокола тоже, так что, для начала --- можно выбрать самый
|
||
простой способ распознать пакет хэндшейка.
|
||
|
||
Но пожалуй, я настою на том, что бы это был какой-то
|
||
динамический способ, что бы не зависеть от статических
|
||
сигнатур.
|
||
|
||
Вычисляться должно быстро, желательно
|
||
в один проход вперёд.
|
||
|
||
|
||
Плюсы:
|
||
|
||
@itemize @bullet
|
||
@item Меньше кода
|
||
@item Не меняется PEX
|
||
@item Нет устойчивых номеров портов
|
||
@item Можно навесить сверху любого протокола,
|
||
например, RPC
|
||
@end itemize
|
||
|
||
Минусы:
|
||
|
||
@itemize @bullet
|
||
@item Потенциально медленнее
|
||
@end itemize
|
||
|
||
@subsection Дизайн
|
||
|
||
Отдельный Proxy на каждый Messaging, с общим KeyStore.
|
||
|
||
@itemize @bullet
|
||
@item Пир сразу идентифицируется для всех протоколов
|
||
@item Можно надстроить над любым Messaing, в частности, над RPC,
|
||
который у нас пока без средств шифрования и аутентификации.
|
||
@end itemize
|
||
|
||
@verbatim
|
||
keys <- newKeyStore
|
||
proxy1 <- newProxyEnc keys (newMessaingUDP ...)
|
||
proxy2 <- newProxyEnc keys (newMessaingTCP ...)
|
||
proxy3 <- newProxyMessaging proxy1 (Just proxy2)
|
||
peer <- newPeer ... (Fabriq proxy3)
|
||
@end verbatim
|
||
|
||
Далее. Предусмотрим два режима: bypass и drop.
|
||
|
||
@subsection Режим bypass
|
||
|
||
Пробует провести handshake, если не удаётся ---
|
||
то оставляет эти попытки и просто пересылвает сообщения,
|
||
как есть на следующий уровень.
|
||
|
||
Хэндшейк делает сам. <<Чужие>> пакеты при этом пропускает
|
||
и наверх, и вниз.
|
||
|
||
Таким образом, остаётся обратная совместимость --- ведь
|
||
пакеты этой прокси просто будут дропнуты.
|
||
|
||
Так же можно пакеты слать культурно, в виде AnyMessage
|
||
с каким-то несуществующим типом протокола, который
|
||
отсутствует в каких-либо обработчиках --- тогда их
|
||
будет видно в логах. Возможно, это излишнее.
|
||
|
||
@subsection Режим drop
|
||
|
||
Пробует провести handshake, если не удаётся ---
|
||
то не делает ничего, либо повторяет их заданное число раз,
|
||
а потом не делает ничего.
|
||
|
||
Этот режим выбирают пиры, которые настаивают на защищенном
|
||
обмене.
|
||
|
||
@subsection Сообщения и FSM
|
||
|
||
Незашифрованное:
|
||
|
||
1. HEY(PREFIX,PKs,PKe,SIGN(PKs, PKe))
|
||
|
||
@table @asis
|
||
|
||
@item PKs:
|
||
ключ подписи пира
|
||
|
||
@item PKe:
|
||
публичный ключ шифрования сессии
|
||
|
||
@end table
|
||
|
||
Зашифрованное:
|
||
|
||
2. HEYOURSELF(PKe,BOX(PKe,Sid,SECRET))
|
||
|
||
@table @asis
|
||
|
||
@item PKe:
|
||
публичный ключ шифрования сессии
|
||
|
||
@item Sid:
|
||
Идентификатор ключа на нашей стороне,
|
||
должен быть в открытом виде в зашифрованном пакете
|
||
|
||
@end table
|
||
|
||
Пересылаем ключ шифрования, теперь пир может слать нам
|
||
зашифрованные сообщения.
|
||
|
||
Просто пытаемся расшифровать их своим секретом. Неудача ---
|
||
просто дропаем или пропускаем, в зависимости от политики.
|
||
|
||
Теперь, если пир продолжает слать незашифрованные
|
||
сообщения --- можем их или дропать, или продолжать
|
||
пропускать.
|
||
|
||
После получения HEYOURSELF мы должны пиру послать
|
||
наш секрет ключ тоже, так что должны видимо, в свою
|
||
очередь ответить HEY.
|
||
|
||
|
||
@verbatim
|
||
|
||
Peer1 Peer2
|
||
***** ****
|
||
| HEY |
|
||
|------------------>|
|
||
| HEYOURSELF |
|
||
|<------------------| Теперь Peer1 может слать Peer2
|
||
| | зашифрованные сообщения
|
||
| |
|
||
| HEY |
|
||
|<------------------| Теперь Peer2 может слать Peer1
|
||
| HEYOURSELF | зашифрованные сообщения
|
||
|------------------>|
|
||
| |
|
||
| |
|
||
|
||
@end verbatim
|
||
|
||
Заметим, что это хорошо ложится на систему обработки
|
||
подпротоколов в Peer: пары HEY/YOUSELF являются
|
||
независимыми, и, кажется, stateless, в том плане,
|
||
что нонсом тут явлется сам PKe.
|
||
|
||
Но! Если это вынести на уровень Peer, то потеряется
|
||
возможность навесить аутентификацию/шифрование на любой
|
||
Messaging.
|
||
|
||
Поэтому делаем на уровне Messaging.
|
||
|
||
Грубо говоря: получили HEYOURSELF --- обновили ключ.
|
||
|
||
Исключительная ситуация:
|
||
|
||
Пир потерял наш SECRET и об этом не знает.
|
||
|
||
Это может случиться только при рестарте, если мы не сохраняем
|
||
ключ.
|
||
|
||
Но это не может случиться, так как, если у ProxyEnc нет ключа
|
||
--- то она посылает HEY.
|
||
|
||
|
||
Замечание: ProxyEnc является пассивной, она ничего не знает про
|
||
пиров, PEX и сама не инициирует общение, так как не знает, с кем
|
||
общаться.
|
||
|
||
Поэтому, в режиме Bypass --- просто пропускает сообщения, как
|
||
есть, в обе стороны, не препятствуя. Как только удалось
|
||
согласовать ключ --- то есть, получить HEYOURSELF --- то
|
||
включаем шифрование.
|
||
|
||
Если пинг прислал нам HEYOURSELF, а сам незашифрованные
|
||
сообщения шлёт -- ну дурак, чо. Надо проинформировать об этом
|
||
в лог, может забанить его.
|
||
|
||
В режиме Drop -- ставит сообщения в очередь, пока не появился
|
||
ключ.
|
||
|
||
Как только ключ появился --- шифрует и отправляет.
|
||
|
||
Пиры авторизуются друг у друга при помощи протокола Ping.
|
||
|
||
Никаких других ходить не должно до этого, это в принципе ошибка.
|
||
|
||
Соответственно, пока пиры не авторизовались --- никакого обмена
|
||
нет.
|
||
|
||
Даже если мы этот пинг дропнем --- пир нас пинганёт еще раз.
|
||
|
||
@strong{Про Sid} Так как один и тот же пир может быть под разными
|
||
адресами одновременно, а PeerNonce и прочее --- на этом уровне протокола
|
||
недоступно --- будем генерировать уникальный ключ для каждого пира по
|
||
адресу, и в HEYOURSELF пересылать Sid ключа. Пир будет сохранять пару
|
||
(Sid, SECRET) --- и в ответе будет брать нужный ключ.
|
||
|
||
Проблема, если мы пишем пиру на один адрес (каким нам ключом шифровать?),
|
||
а он отвечает нам с другого адреса.
|
||
|
||
PeerNonce недоступен, что делать?
|
||
|
||
Кейс:
|
||
|
||
Peer1: Peer2:
|
||
tcp:1.1.1.1:8957 -> tcp:1.1.10.5:8001
|
||
<- tcp:1.1.10.5:62511
|
||
|
||
|
||
В HEYO(SELF) посылаем ему ID ключа, он где-то (где?) запоминает,
|
||
что нам надо слать c таким ID ключа.
|
||
|
||
|
||
|
||
@bye
|
||
|
||
|