Marafon Protocol это протокол для одноимённого полнофункционального мессенджера, работающего поверх TCP. Основной фокус при работе над оным идёт на:
Marafon Protocol это протокол для одноимённого полнофункционального мессенджера, работающего поверх TCP (в будущем будет добавлена поддержка UDP и QUIC). Основной фокус при работе над оным идёт на:
- Снижение оверхэдов, по сравнению с классическими решениями (Matrix, Discord, WhatsApp, пр.).
- Устойчивость к цензуре.
@@ -13,29 +13,52 @@ Marafon Protocol это протокол для одноимённого пол
## Пакеты
## Поведение сервера и клиента
Существует три уровня категорий пакетов:
Сервер **обязан**:
1. Оповещать клиент и сервера в федерации о всех ошибках, кроме:
- Связанных с безопасностью
- Связанных с внутренними неполадками
2. Отдавать предпочтение данным других серверов, нежели клиентов.
3. Отдавать предпочтение сетевым настройкам входящих соединений, нежели локальным (не считая лимиты).
Клиент **обязан**:
1. Хранить тишину о всех ошибках.
2. Явно уведомлять юзера при возникновении проблем с безопасностью, как минимум по умолчанию.
Надкатегория никак не указывается в пакете и зависит от контекста. Он-же определяет структуру некоторых пакетов, которая может менятся в конкретных случаях. Остальные две занимают по одному байту соответственно.
Надкатегория никак не указывается в пакете, зависит от контекста и при упоминании - чаще всего опускается. Она также определяет структуру некоторых событий, которая может меняться в конкретных случаях. Остальные две занимают по одному байту соответственно.
Пакеты всех категорий, кроме явно оговорённых или принадлежащих к надкатегории Server2Client, содержат идентификатор серверной сессии, являющийся четырёхбайтным числом без знака.
Все категории вместе являются типом события. Значение типа - это число, которое указывается в начале пакета.
Пакеты всех категорий из надкатегории Client2Server, кроме явно оговорённых, содержат хэш полезной нагрузки, зашифрованный с помощью закрытого ключа подписи клиента.
События всех категорий, кроме явно оговорённых или принадлежащих к надкатегории Server2Client, содержат идентификатор серверной сессии, являющийся четырёхбайтным числом без знака (`uint64_t`).
Данные (AKA "полезная нагрузка") являются расположенными последовательно парами "ключ-значение" и могут быть расположены относительно друг-друга в произвольном порядке. Они следующий формат:
События всех категорий из надкатегории Client2Server, кроме явно оговорённых, содержат хэш полезной нагрузки, зашифрованный с помощью закрытого ключа подписи клиента.
`[2b: key][2b: data length][>0 b: data]`
Данные (AKA "полезная нагрузка") являются расположенными последовательно парами "ключ-значение" и могут быть расположены в пакете относительно друг-друга в произвольном порядке. Одна пара имеет следующий формат:
`[1b: key][2b: data length][>0 b: data]`
Все пакеты размером ниже установленного дополняются псевдослучайной последовательностью байт. (???)
<!-- ATTENTION: ^ под вопросом ^ -->
Исходя из всего вышеописанного, итоговая примерная структура пакета выглядит следующим образом:
`[1b: category][1b: subcategory][4b: server session][>0b: payload]`
`[1b: category][1b: subcategory][4b: server session][?b: payload hash][>0b: payload][~b: random bytes]`
Размер пакета определяется ответом сервера на этапе обмена характеристиками, но не может быть больше, чем 65535 байт. До этого этапа размер пакета составляет 512 байт.
Размер пакета определяется запросом клиента на этапе обмена характеристиками, но не может быть больше, чем 65535 байт или меньше 128 байт. До этого этапа (включительно) размер пакета составляет 512 байт.
Магическим числом протокола является следующая последовательность байт:
После успешной установки защищённого TLS соединения начинается этап обмена характеристиками. Клиент отправляет некоторые метаданные, в числе которых:
Первичное подключение к серверу может выполнятся разными способами, в том числе подразумевающими маскировку траффика под существующие протоколы, но при использовании вне локальных сетей - должно сводиться к установке защищённого соединения. Данная версия протокола подразумевает поддержку пока лишь двух способов подключения: напрямую (без шифрования) и с использованием TLS-over-TCP.
- Магическое число: 8 байт
- Версия протокола: 4 байта
- ID серверной сессии: 4 байта (0 если нет; на случай, если соединение надо восстановить, а не создать с нуля)
После успешной установки защищённого соединения, происходит обмен характеристиками, AKA "рукопожатие". Клиент отправляет событие типа `Handshake.Request`, на что сервер отвечает либо ошибкой, либо событием типа `Handshake.Accepted`.
<!-- TODO: надо ещё обдумать, какими данными обмениваться клиентосерверу -->
Если при обмене метаданными клиент или сервер обнаруживают несоответствие магических чисел, то они разрывают соединение. Если сервер не поддерживает указанную клиентом версию протокола - он отвечает ошибкой и разрывает соединение. Если при попытке восстановить соединение клиентом, ID серверной сессии не существует в списке недавно-разорванных, то он отвечает ошибкой и разрывает соединение.
Сервер отвечает ошибкой `Error.HandshakeFailed` и разрывает соединение, если:
- При обмене данными клиент или сервер обнаруживают несоответствие магических чисел.
- Сервер не поддерживает указанную клиентом версию протокола.
- Невозможно выделить новый порт для подключения.
- Указанный транспортный протокол не поддерживается.
- Размер пакета не попадает в рамки жёсткого лимита сервера.
Если при хэндшейке клиентом был указан новый способ подключения и получен валидный ответ от сервера - то они обязаны разорвать текущее соединение, затем, спустя случайное количество времени, от 50 мс до 500 мс, создать новое.
### Регистрация
При успешной регистрации учётной записи, клиент генерирует пару ключей (открытый-закрытый) в качестве подписи, после чего открытый ключ отправляется серверу и тот сохраняет его.
### Вход в учётную запись
Вход в учётную запись происходит путём отправки логина и хэша пароля на сервер. При этом клиент может указать ID серверной сессии, если произошло непредвиденное отключение и ему необходимо переподключиться.
Если логин или пароль несовпадают - сервер отвечает ошибкой и разрывает соединение. Если полезная нагрузка подписана ложно - сервер отвечает ошибкой, соединение разрывается, а администратор сервера и владелец учётной записи уведомляются об инциденте.
Если ID серверной сессии нет в списке недавно-разорванных - сервер отвечает ошибкой и разрывает соединение.
При успешном входе в учётную запись, сервер генерирует `uint32_t`, являющийся идентификатором серверной сессии. Клиент обязан хранить его и прикреплять к каждому пакету (кроме особо-оговорённых случаев) до момента отключения от сервера.
Если при проверке хэша полезной нагрузки пакета обнаруживается, что подпись неверна - сервер отвечает ошибкой, соединение разрывается, а администратор сервера и владелец учётной записи уведомляются об инцеденте.
### Пользовательские сессии и подпись
Если при проверке ID серверной сессии обнаруживается несоответствие - сервер отвечает ошибкой, соединение разрывается.
Каждый принятый сервером пакет, содержащий хэш полезной нагрузки, должен проверяться на соответствие подписи. Если сервер обнаруживает, что подпись неверна - сервер отвечает ошибкой, соединение разрывается, а администратор сервера и владелец учётной записи уведомляются об инциденте.
Каждые n секунд клиент отправляет запрос на обновление статуса аккаунта. Если спустя указанное на этапе обмена характеристиками время нет ни одного пакета от клиента - сервер разрывает соединение.
Каждый принятый клиентом пакет, содержащий хэш полезной нагрузки, может быть проверен на соответствие подписи. Если клиент обнаруживает, что подпись неверна - он должен оповестить пользователя.
<!-- TODO: решить: должен ли сервер иметь свою подпись; как и нужно-ли, чтобы сервер являлся средой нулевого доверия в плане передачи подписей пользователей (скорее всего "Да." х 2) -->
Полезная нагрузка пакета может быть проверена на целостность сервером или клиентом с использованием подписанного хеша, но данная проверка может быть отключена, по усмотрению владельца сервера или пользователя клиента.
Если при проверке ID серверной сессии обнаруживается несоответствие - сервер отвечает ошибкой, соединение разрывается.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.