From ed54ecb7f9d01cd6f9234641ab01dec4ff9d470e Mon Sep 17 00:00:00 2001 From: shr3dd3r Date: Mon, 11 Mar 2024 21:30:22 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A2=D0=BE=D1=82=D0=B0=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B4=D0=B5=D0=BB,=20?= =?UTF-8?q?=D0=B3=D0=BB=D0=B0=D0=B2=D0=B0=20=D0=B2=D1=82=D0=BE=D1=80=D0=B0?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 12 +- Common behavior.md | 24 --- Data types.md | 3 - Data types/Crypto_Algorithm.md | 20 --- Data types/Crypto_AlgorithmID.md | 71 -------- Data types/Crypto_AlgorithmParams.md | 13 -- Data types/EventAsyncID.md | 9 - Data types/LocalObjectID.md | 9 - Events.md | 45 ----- Handshake.md | 147 ----------------- Overview.md | 92 ----------- README.md | 22 +-- Reserved events.md | 21 --- Reserved events/Example.md | 43 ----- SPX/Marafon/README.md | 3 - Streams.md | 26 --- Оглавление.txt | 51 ++++++ Рукопожатие.md | 236 +++++++++++++++++++++++++++ 18 files changed, 297 insertions(+), 550 deletions(-) delete mode 100644 Common behavior.md delete mode 100644 Data types.md delete mode 100644 Data types/Crypto_Algorithm.md delete mode 100644 Data types/Crypto_AlgorithmID.md delete mode 100644 Data types/Crypto_AlgorithmParams.md delete mode 100644 Data types/EventAsyncID.md delete mode 100644 Data types/LocalObjectID.md delete mode 100644 Events.md delete mode 100644 Handshake.md delete mode 100644 Overview.md delete mode 100644 Reserved events.md delete mode 100644 Reserved events/Example.md delete mode 100644 SPX/Marafon/README.md delete mode 100644 Streams.md create mode 100644 Оглавление.txt create mode 100644 Рукопожатие.md diff --git a/.gitignore b/.gitignore index ce0aa93..997dd27 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,2 @@ -Data types/DATA TYPES.md -SPX/ -_something.md -LBM.md -Reserved LBM keys.md -Reserved events/idk/ -Reserved events/BaseResponse/ -Reserved events/BaseSession/ -Reserved events/BaseUnsigned/ -Sessions.md +_old/* +1.2. Терминология.md diff --git a/Common behavior.md b/Common behavior.md deleted file mode 100644 index 0093977..0000000 --- a/Common behavior.md +++ /dev/null @@ -1,24 +0,0 @@ -# Общие принципы поведения узлов - - - -## Обязанности узла - -1. Оповещать об ошибке, возникшей во время обработки _события_ другого _узла_, но только если ошибка связана с его некорректной формой. -2. По умолчанию отклонять все _события_ с ложной подписью в _подписанном соединении_. - - - -## Обязанности сервера - -1. Оповещать _узел_ (будь то _клиент_ или _сервер_ в федерации) о всех ошибках, возникших во время обработки его _события_, кроме связанных с безопасностью. -2. Отдавать предпочтение данным других _серверов_ в федерации, нежели _клиентов_. -3. Отдавать предпочтение сетевым настройкам входящих соединений, нежели локальным (не считая лимиты). - - - -## Обязанности клиента - -1. Не сообщать _серверу_ ни о каких ошибках на своей стороне. -2. По умолчанию блокировать до решения юзера обработку любых _событий_, содержащих ложную подпись. -3. Явно уведомлять юзера при возникновении проблем с безопасностью, как минимум по умолчанию. diff --git a/Data types.md b/Data types.md deleted file mode 100644 index fed3b77..0000000 --- a/Data types.md +++ /dev/null @@ -1,3 +0,0 @@ -# Структуры данных - -Сия спецификация, помимо всего прочего, также упоминает и определяет некоторые необходимые типы и структуры данных. В директории `Data types/` вы найдёте их описания и примеры декларации на языке C++. diff --git a/Data types/Crypto_Algorithm.md b/Data types/Crypto_Algorithm.md deleted file mode 100644 index 0b930f0..0000000 --- a/Data types/Crypto_Algorithm.md +++ /dev/null @@ -1,20 +0,0 @@ -# Crypto::Algorithm - -Описывает криптографический алгоритм вместе с его базовой конфигурацией. - -## Структура - -`[AlgorithmID: 1B][AlgorithmParams: 1B]` - -## C++ - -```C++ -namespace Crypto { - -struct Algorithm { - AlgorithmID ID; - AlgorithmParams Params; -}; - -} -``` \ No newline at end of file diff --git a/Data types/Crypto_AlgorithmID.md b/Data types/Crypto_AlgorithmID.md deleted file mode 100644 index 309d1a6..0000000 --- a/Data types/Crypto_AlgorithmID.md +++ /dev/null @@ -1,71 +0,0 @@ -# Crypto::AlgorithmID - -Является перечислением однобайтных целых чисел без знака, соответствующих идентификаторам допустимых к использованию в базовом протоколе криптографических (и не только) алгоритмов: хэшей, контрольных сумм, симметричного и ассиметричного шифрования. - - - -## C++ - -```C++ -namespace Crypto { - -enum struct AlgorithmID : uint8_t { - None = 0, -// Checksums - CRC = 11, // 8/16/32/64/etc. - fletcher, // 8/16/32/etc. - Adler, // 32/etc. -// Non-crypto hashes - Murmur64A = 31, - Murmur3, // 32/128 - Spooky, // 128/etc. -// Cryptographic hashes - BLAKE2b = 51, - BLAKE3, - GOST, - HAS160, - HAVAL, - MD2, - MD5, - RIPEMD, - SHA1, - SHA2, - SHA3, - SHAKE, // 128/256 - Skein, - Snefru, - Streebog, - Tiger, - Whirlpool, -// Symmetric key block ciphers - Blowfish = 81, - Twofish, - Threefish, - TripleDES, - HPC, - MARS, - SAFER2PLUS, - SQUARE, - CRYPTON, - AES, // AKA Rijndael, GCM/CBC/CTR/etc. - MAGENTA, - XTEA1, - XTEA3, - KHAZAD, - Camellia, - Salsa20, - CAST5, - CAST6, - Kuznyechik, - MESH, - Akelarre, - RC6 -}; - -} -``` - - diff --git a/Data types/Crypto_AlgorithmParams.md b/Data types/Crypto_AlgorithmParams.md deleted file mode 100644 index 86ed49b..0000000 --- a/Data types/Crypto_AlgorithmParams.md +++ /dev/null @@ -1,13 +0,0 @@ -# Crypto::AlgorithmParams - -Описывает параметры алгоритма, например размер ключа. Является двухбайтным целым без знака. - -## C++ - -```C++ -namespace Crypto { - -typedef uint16_t AlgorithmParams; - -} -``` diff --git a/Data types/EventAsyncID.md b/Data types/EventAsyncID.md deleted file mode 100644 index 7f4e761..0000000 --- a/Data types/EventAsyncID.md +++ /dev/null @@ -1,9 +0,0 @@ -# EventAsyncID - -Является уникальным двухбайтным целым числом без знака и предназначен для определения отношения запросов к ответам при асинхронном обмене _событиями_. - -## C++ - -```C++ -typedef uint16_t EventAsyncID; -``` diff --git a/Data types/LocalObjectID.md b/Data types/LocalObjectID.md deleted file mode 100644 index 43035fb..0000000 --- a/Data types/LocalObjectID.md +++ /dev/null @@ -1,9 +0,0 @@ -# LocalObjectID - -Уникальный идентификатор объекта на сервере, является восьмибайтным числом без знака. - -## C++ - -```C++ -typedef uint64_t LocalObjectID; -``` diff --git a/Events.md b/Events.md deleted file mode 100644 index f95f2d0..0000000 --- a/Events.md +++ /dev/null @@ -1,45 +0,0 @@ -# События - - - -## Категории и типы - -Существует три уровня категорий _событий_: - -**Надкатегория** описывает виды взаимодействующих _узлов_. Не указывается и зависит от контекста. Варианты: _Client2Server_ (_событие_ сгенерированное _клиентом_ для _сервера_), _Server2Client_ (_событие_ сгенерированное _сервером_ для _клиента_), _Peer2Peer_ (_событие_ сгенерированное _узлом_ для другого _узла_ того-же ранга, в том числе это касается _серверов_ в федерации). - -**Категория** описывает _события_ одного класса. Является однобайтным числом без знака. Всегда больше нуля. Например: _Authentication_, _Object_, т.п. - -**Подкатегория** описывает конкретное _событие_ из класса, соответствующего категории. Является однобайтным числом без знака. Всегда больше нуля. Например: _Login_, _GetContents_, т.п. - -Все категории вместе - являются типом _события_. Информацию про зарезервированные типы вы можете найти в [Reserved events.md](Reserved%20events.md). - - - -## Содержание и структура - -Все _события_ содержат идентификатор _потока_ размером 4 байта. Подробнее - в [Streams.md](Streams.md). - -_События_ в _шифрованном соединении_ содержат хэш, применённый к зашифрованному блобу (см. ниже). Этот хэш имеет размер равно или более 16 байт и подписан с помощью закрытого ключа подписи отправляющего. Он гарантирует достоверность содержания _события_ на уровне прямого подключения между двумя _узлами_. Используемые алгоритмы подписи и хэширования определяются на этапе рукопожатия. - -Все _события_ содержат асинхронный идентификатор _события_, который является двухбайтным целым числом без знака и предназначен для определения отношения запросов к ответам при асинхронном обмене _событиями_ в конкретном _потоке_. - -Все _события_ содержат тип (см. выше раздел "Категории и типы"). - -_События_ могут содержать полезную нагрузку, либо один нулевой байт, свидетельствующий о её отсутствии. Данная версия протокола не накладывает ограничений на формат полезной нагрузки, за исключением базовых событий, которые представлены данными в формате _LBM_. Описание всех предопределённых в базовом протоколе ключей ячеек _LBM_ доступно в [Reserved LBM keys.md](Reserved%20LBM%20keys.md). При использовании формата _LBM_, в полезную нагрузку могут добавляться шумовые данные. Неизвестные ключи при обработке полезной нагрузки игнорируются. - -_События_ в _шифрованном соединении_ содержат блоб, зашифрованный с помощью актуального ключа _потока_ и утверждённого алгоритма симметричного шифрования. Этот блоб содержит асинхронный идентификатор, тип события и полезную нагрузку. В нешифрованном _соединении_, указанные поля находятся в "сыром" виде. - -Исходя из всего вышеописанного, минимальный размер сериализованного в бинарный вид _события_ в нешифрованном _соединении_ составляет 9 байт, а его итоговая структура выглядит следующим образом: - -`[Stream ID: 4B][Async ID: 2B][Event Type: 2B][Payload: >0B]` - -В свою очередь, _события_ в _шифрованном соединении_ имеют размер не менее 25-и байт и следующую структуру: - -```text -[Stream ID: 4B][Signed Hash: >=16B][Encrypted Blob: >=5B] - / | \ - [Async ID: 2B][Event Type: 2B][Payload: >0B] -``` - -Максимальный размер _события_ не нормирован. Ответственность за менеджмент разделения _события_, в случае, если оно не помещается в один пакет, остаётся на транспортном уровне. Максимальный размер полезной нагрузки _события_ определяется на этапе рукопожатия. diff --git a/Handshake.md b/Handshake.md deleted file mode 100644 index ea992c2..0000000 --- a/Handshake.md +++ /dev/null @@ -1,147 +0,0 @@ -# Рукопожатие - -На рукопожатие возложена задача выяснить способность _узлов_ устанавливать соединение по описываемому протоколу и согласовать подключение с избранными параметрами каждой стороны. На выбор существует два типа хэндшейка: открытый и зашифрованный. Второй дополнительно требует знать публичный ключ целевого _узла_. - - - -## Запрос рукопожатия - -После успешного подключения к целевому _узлу_ через избранный транспорт происходит обмен параметрами обоих сторон. Инициирующий соединение _узел_ отправляет пакет с магический числом, флагом шифрования и сериализованными данными в формат _LBM_. В случае с зашифрованным хэндшейком, сериализованные данные дополнительно зашифровываются с помощью публичного ключа целевого _узла_. - -Структура пакета с запросом рукопожатия выглядит следующим образом: - -`[magic number: 8B][encryption flag: 1B][LBM-formatted data: >0B]` - -### Магическое число - -Магическим числом является следующая последовательность байт: - -``` -HEX: 0x53 0x74 0x61 0x64 0x69 0x75 0x6d 0x50 -DEC: 83 116 97 100 105 117 109 80 -ASCII: S t a d i u m P -``` - -Если _узел_ получает пакет, который не содержит магического числа на указанной позиции, то он должен молча закрыть соединение. - -### Флаг шифрования - -Запрос шифрованного хэндшейка предполагает установку этого флага в значение `0x7a`. Все остальные значения должны интерпретироваться как запрос открытого хэндшейка. - -### Данные - -Данные в формате _LBM_ содержат следующие ячейки: - -- Версия протокола - - _Ключ:_ `0x01` - - _Тип:_ `uint8_t` - - _Обязательна:_ да - - Поддерживаемая запрашивающим _узлом_ версия протокола. - - `0b10000000`: зарезервировано - - `0b01111111`: номер версии - -- Алгоритмы хэширования полезной нагрузки - - _Ключ:_ `0x02` - - _Тип:_ `Crypto::Algorithm[]` - - _Обязательна:_ только для шифрованного ХШ - - Описывает запрашиваемые к использованию в этом соединении хэш-функции для формирования хэша полезной нагрузки. - -- Алгоритм подписи - - _Ключ:_ `0x03` - - _Тип:_ `Crypto::Algorithm` - - _Обязательна:_ только для шифрованного ХШ - - Описывает используемый запрашивающим _узлом_ алгоритм подписи. - -- Публичный ключ подписи - - _Ключ:_ `0x04` - - _Тип:_ `char[]` - - _Обязательна:_ только для шифрованного ХШ - - Публичный ключ подписи запрашивающего _узла_, который будет использован для проверки достоверности событий в соединении. - -- Алгоритмы симметричного шифрования - - _Ключ:_ `0x05` - - _Тип:_ `Crypto::Algorithm[]` - - _Обязательна:_ только для шифрованного ХШ - - Описывает запрашиваемые к использованию в этом соединении симметричные алгоритмы шифрования. - -- Приватный ключ - - _Ключ:_ `0x06` - - _Тип:_ `char[]` - - _Обязательна:_ только для шифрованного ХШ - - Используемый в этом соединении приватный ключ, размера соответствующего избранному алгоритму симметричного шифрования. - -- Параметры шума - - _Ключ:_ `0x07` - - _Тип:_ `uint8_t` - - _Обязательна:_ нет - - Описывает параметры шума в соединении: - - `0b11100000`: выделено под настройку количества _шумовых пакетов_ - - `0b000`: отсутствие _шумовых пакетов_ - - `0b001`: соотношение _шумовых пакетов_ к реальным - 1:8 - - `0b010`: соотношение - 1:4 - - `0b011`: 1:2 - - `0b100`: 1:1 - - `0b101`: 2:1 - - `0b110`: 4:1 - - `0b111`: зарезервировано - - `0b00011000`: выделено под настройку размера _шумовых пакетов_ - - `0b00`: случайный размер, в пределах от минимального _пакета_ с нулевой полезной нагрузкой, до размера _пакета_ с полезной нагрузкой размерности, которую должен передать целевой _узел_ при одобрении рукопожатия - - `0b01`: случайный размер, на основе среднего размера передаваемого пакета +- 25% (но всегда меньше максимального) - - `0b10`: случайный размер, в пределах от минимального, до максимального / 2 - - `0b11`: зарезервировано - - `0b00000110`: выделено под настройку размера _шумовых данных_ в обычных _пакетах_ - - `0b00`: без шумовых данных - - `0b01`: заполнение шумовыми данными до лимита полезной нагрузки - - `0b10`: заполнение шумовыми данными в случайном количестве, от нуля, до лимита полезной нагрузки - - `0b11`: зарезервировано - - `0b00000001`: зарезервировано - - - - - -## Ответ на рукопожатие - -На запрос рукопожатия с корректным магическим числом целевой _узел_ должен ответить пакетом либо с согласием, либо с ошибкой. Если это ответ с согласием, то данные в пакете должны быть зашифрованы с помощью переданного в запросе хэндшейка симметричного шифра и переданного приватного ключа. Если это ответ с ошибкой, то он может быть незашифрован, в случае, если ошибка связанна с инициализацией шифрования или версией протокола. Таким образом, запрашивающий _узел_ должен быть готов обработать оба варианта развития событий, исходя из значения флага шифрования. Если цель не поддерживает указанные при рукопожатии параметры, запрашивающий может попробовать установить соединение снова, с иными параметрами. - -Пакет ответа имеет следующую структуру: - -`[magic number: 8B][encryption flag: 1B][LBM-formatted data: >0B]` - -### Данные - -Данные в формате _LBM_ содержат следующие ячейки: - -- Максимальный размер полезной нагрузки - - _Ключ:_ `0x71` - - _Тип:_ `uint16_t` - - _Обязательна:_ только для соглашения - - Всегда больше нуля. Оба _узла_ должны соблюдать размерность полезной нагрузки в событии, она всегда должна быть равна или меньше этого значения. - -- Код ошибки - - _Ключ:_ `0x72` - - _Тип:_ `uint8_t` - - _Обязательна:_ только для ошибки - - Код, описывающий тип ошибки: - - `0x01`: магическое число ложно - - `0x02`: неподдерживаемая версия протокола - - `0x03`: недопустимый алгоритм хэширования - - `0x04`: недопустимый алгоритм подписи - - `0x05`: недопустимый алгоритм симметричного шифрования - - `0x06`: некорректный приватный ключ - - `0x07`: некорректный публичный ключ подписи - - `0x08`: недопустимые параметры шума - - `0x09`: невозможно корректно расшифровать данные (ложный публичный ключ целевого _узла_) - -- Описание ошибки - - _Ключ:_ `0x73` - - _Тип:_ `char[]` - - _Обязательна:_ нет - - Строка в кодировке ASCII, описывающая ошибку в человекочитаемом виде. - - diff --git a/Overview.md b/Overview.md deleted file mode 100644 index 6c589da..0000000 --- a/Overview.md +++ /dev/null @@ -1,92 +0,0 @@ -# Обзор - - - -## Терминология - -Здесь перечислены используемые в данной спецификации термины, значение которых может быть не очевидно и/или не соответствует тому, которое подразумевается обычно. - -### Узел - -Любая машина, способная выполнять коммуникацию по протоколу Stadium. Обычно используется в контексте коммуникации двух машин одного ранга или когда ранг не имеет значения. - -### Сервер - -Выполняющий роль сервера _узел_ в многоранговой сети. - -### Клиент - -Выполняющий роль клиента _узел_ в многоранговой сети. - -### Соединение/поток - -Канал обмена _событиями_ между двумя _узлами_, имеющий свой идентификатор (Stream ID/SID, см. [Streams.md](Streams.md)) и набор настроек. - -### Шифрованное соединение/поток - -_Соединение_ между двумя _узлами_, все _события_ в котором шифруются с помощью оговорённого симметричного алгоритма и ключа, а также подписываются с помощью приватного ключа отправителя. - -### Сессия - -То, что устанавливается при создании первого _соединения_ между двумя _узлами_, с чем ассоциированы конкретные _потоки_ и прочие параметры. - -### Событие - -Структура данных, обладающая конкретными семантическими свойствами в зависимости от типа и содержащая дополнительные поля, требуемые для верного отображения и интерпретации самой структуры. Проще говоря - то, чем обмениваются и что обрабатывают _узлы_ во время коммуникации между собой. - -### Шумовое событие - -_Событие_ предопределённой категории, имеющее случайно-сгенерированные поля. - -### LBM - -Аббревиатура наименования способа форматирования (сериализации) данных, используемого для полезной нагрузки _события_, которая расшифровывается как "Linear Binary Map". Подробнее - в [LBM.md](LBM.md). - -### Шумовые данные - -Случайно-сгенерированные данные, помещённые в специализированную ячейку полезной нагрузки _события_. - - - -## Пример коммуникации двух узлов - -Допустим, что у нас есть два неизвестных\* друг-другу _узла_ - `p1` и `p2`. Тогда полная коммуникация по шагам (от подключения неизвестного для `p2` _узла_ - до "правильного" уничтожения сессии и закрытия соединения) будет выглядеть так: - -1. `p1` запрашивает открытое (нешифрованное) рукопожатие с избранными параметрами соединения у `p2` -2. `p2` отвечает `p1` согласием со своей частью параметров -3. `p1` запрашивает публичный ключ подписи у `p2` -4. `p2` передаёт свой публичный ключ, вместе с используемым алгоритмом -5. `p1` сохраняет полученный публичный ключ и разрывает\*\* соединение и заново устанавливает его, путём отправки `p2` запроса шифрованного хэндшейка -6. `p2` отвечает согласием со своей частью параметров подключения -7. `p1` отправляет зашифрованное _событие_ с запросом какого-то объекта -8. `p2` отвечает зашифрованным _событием_, содержащее статус-код или этот объект -9. `p1` отправляет _событие_ с сообщением о намерении уничтожить сессию и ждёт в течении n времени, по истечению которого может разорвать соединение "насильно" -10. `p2` принимает запрос и прекращает любой параллельный обмен данными -11. `p2` отвечает _событием_ с кодом успешности операции -12. `p2` закрывает соединение - -И в виде схемы: - -``` -Requesting p2's public key: -[p1] ------[raw handshake request]----> [p2] -[p1] <-----[raw handshake accept]------ [p2] -[p1] ----[request public sign key]----> [p2] -[p1] <-[public key and crypto params]-- [p2] -[p1] ---------[close session]---------> [p2] -[p1] <--------[status code OK]--------- [p2] - -Reconnecting in encrypted way and requesting object: -[p1] ---[encrypted handshake request]-> [p2] -[p1] <--[encrypted handshake accept]--- [p2] -[p1] ------[request some object]------> [p2] -[p1] <-[some object data/status code]-- [p2] -[p1] ---------[close session]---------> [p2] -[p1] <--------[status code OK]--------- [p2] -``` - -\* - _стоит уточнить, что в реальных сценариях использования публичный ключ целевого узла скорее всего уже известен, либо коммуникация должна производиться через транспорт, гарантирующий достоверность передаваемых данных. `p1` может узнать о существовании таковых, путём отправки события специального типа. В данном примере сей шаг опущен._ - - - -\*\* - _"разрыв" соединения предполагает обмен между узлами событиями с информацией о завершении сессии; в данном пункте эти шаги опущены, но упомянуты ниже._ diff --git a/README.md b/README.md index 0f1c380..945543a 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,12 @@ -# Спецификация протокола Stadium версии 1.0 +# Неформальная спецификация протокола Stadium версии 0.5 -Stadium это протокол для безопасной коммуникации общего назначения, работающий поверх обширного перечня транспорта и архитектурно не зависящий от оного. Данная спецификация описывает базу, на основе которой могут быть реализованы расширения (_SPX - Stadium Protocol eXtension_) для более конкретных нужд. Помимо прочего, данный протокол служит основой для полнофункционального мессенджера Marafon. +Stadium это протокол для безопасной коммуникации общего назначения, работающий поверх обширного перечня транспорта и архитектурно не зависящий от оного. Данная спецификация описывает базу, на основе которой могут быть реализованы расширения (_SPX - Stadium Protocol eXtension_) для более конкретных нужд. -Основной фокус при работе над сим проектом идёт на: +Основной фокус при работе над этим проектом идёт на: -- Возможность функционирования в условиях низкой пропускной способности канала; -- Устойчивость к цензуре; -- Поддержка широкого спектра транспортных протоколов; -- Построение децентрализованных меш-сетей (как одноуровневых, так и двухуровневых); -- Гибкое сквозное шифрование; -- Расширяемость и возможность подстраивания под конкретные задачи. +- Низкий оверхэд и возможность функционирования в условиях низкой пропускной способности канала; +- Поддержка широкого спектра транспортных протоколов из коробки; +- Гибкое и кастомизируемое сквозное шифрование; +- Расширяемость и возможность подгонки под конкретные задачи. - - -**ПРОЕКТ В АКТИВНОЙ РАЗРАБОТКЕ/PROJECT UNDER ACTIVE DEVELOPMENT** - -All text will be translated to english later. +**Внимание!** В данный момент проект переживает глобальные архитектурные изменения. Данная версия спецификации акцентирует внимание на закреплении общих, преимущественно фундаментальных идей и концепций, потому и является *неформальной*. diff --git a/Reserved events.md b/Reserved events.md deleted file mode 100644 index bdb10d0..0000000 --- a/Reserved events.md +++ /dev/null @@ -1,21 +0,0 @@ -# Зарезервированные типы событий - -Некоторые категории _событий_ зарезервированы под нужды базового протокола или просто для _событий_ определённого класса. Второе носит рекомендательный характер; вы также можете использовать иные диапазоны для тех-же целей. Ниже приведены диапазоны зарезервированных значений в виде шестнадцатеричных чисел. Конкретный перечень всех зарезервированных событий и описание их структуры - есть в папке `Reserved events/`. - - -Зарезервировано для нужд протокола и запрещено к использованию в сторонних расширениях: - -1. Категория и субкатегория `0x00` - - Запрещены к использованию. - -2. Категория `0x01` - - Все субкатегории: выделены для базовых _событий_, связанных с менджментом сессии. - -3. Категория `0x11` - - Все субкатегории: выделены для базовых кодов ответа, ошибок и предупреждений. - - -Рекомендуется к использованию расширениями в конкретных ситуациях: - -1. Категории `0x12`-`0x1F` (включительно) - - Все субкатегории: для кодов ответа, ошибок и предупреждений сторонних реализаций. diff --git a/Reserved events/Example.md b/Reserved events/Example.md deleted file mode 100644 index c78d05b..0000000 --- a/Reserved events/Example.md +++ /dev/null @@ -1,43 +0,0 @@ -# Категория.Субкатегория - -**HEX:** `BA98` - -**Возможные ответы:** [ДругаяКатегория.ДругаяСубкатегория](Example.md), [ЕщёКатегория.ЕщёСубкатегория](Example.md) - - - -## Client2Server - -Какое-то описание типа _события_. Разделы (Client2Server, Server2Client, т.д.) могут находится в произвольном порядке относительно друг-друга. Если ячеек нет, то это указывается явно. - -### Ячейки - -#### ObjectID - -Какое-то описание, к чему применимо значение в этой ячейке, что оно из себя представляет и тому подобное. - -#### PrevEventID - -Тоже какое-то описание. - - - -## Server2Client - -Какие-то пояснении об отличии этого _события_ от иных версий. Если отличий нет, то раздел опускается. Если надкатегория _события_ в этом контексте не допустима, то об этом явно указывается. (см. раздел Peer2Peer для примера) - -### Дополнительные ячейки - -#### CryptoAsymCipher - -Ещё какое-то описание. - -### Отсутствующие ячейки - -- **ObjectID**: описание причины, почему не требуется - - - -## Peer2Peer - -_Не применимо._ diff --git a/SPX/Marafon/README.md b/SPX/Marafon/README.md deleted file mode 100644 index 76ef3f8..0000000 --- a/SPX/Marafon/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Marafon SPX - -_Расширение протокола Stadium для мессенджера Marafon._ \ No newline at end of file diff --git a/Streams.md b/Streams.md deleted file mode 100644 index 0fe4800..0000000 --- a/Streams.md +++ /dev/null @@ -1,26 +0,0 @@ -# Потоки - -**Поток** представляет из себя канал обмена _событиями_ в определённой конфигурации. _Узлы_ должны обрабатывать _события_ единообразно, вне зависимости от того, через какой _поток_ они передаются. - -## Stream ID - -Каждый _поток_ имеет пару собственных уникальных идентификаторов: "Stream ID", "SID". Они используются для определения принадлежности _события_ к существующим _потокам_. Для увеличения энтропии, с целью исключения анализа траффика, SID представляет из себя нефиксированное значение, уникальное для каждого _события_. Каждый из двух _узлов_ в _соединении_ имеет собственный SID. - -Допустим, что есть два _узла_: `pc`, который подключается, и `ps`, к которому подключается `pc`. Тогда пусть SID, используемый _узлом_ `pc` будет называться `sidc`, а `ps` - `sids`. Для того, чтобы установить новое _соединение_, _узлы_ должны проделать следующее: - -1. `pc` генерирует стартовое значение `sidc` и отправляет его _узлу_ `ps` вместе с алгоритмами симметричного шифрования и хэширования для обработки `sids`, а также симметричный ключ соответствующего алгоритму размера. -2. `ps` принимает данные и отвечает _узлу_ `pc` стартовым значением `sids`, алгоритмами шифрования и хэширования `sidc`, а также симметричный ключ соответствующего алгоритму размера. - -При этом, `ps` должен иметь единую конфигурацию алгоритмов симметричного шифрования, хэш-функции и используемый ключ для обработки SID всех _потоков_. Перед использованием в отправляемом _событии_, _узел_ должен обработать прежнее значение SID, а именно: зашифровать его с использованием избранного при установке _соединения_ алгоритма симметричного шифрования и ключа, а затем вычислить хэш. Если размер выходных данных хэш-функции больше размера SID, то эти данные поблочно XOR'ятся сами с собой. В реализации это может выглядеть вот так: - -```C -// size_t arr_sz: размер вывода хэш-функции -// char *arr: вывод хэш-функции -// uint32_t sid: прежний SID -sid = *(uint32_t*)arr; -for (size_t i = 1; i < arr_sz / 4; ++i) - sid = sid ^ ((uint32_t*)arr)[i]; -``` - -Если любой из _узлов_ обнаруживает ложное значение SID в полученном _событии_, то он обязан разорвать _соединение_. - diff --git a/Оглавление.txt b/Оглавление.txt new file mode 100644 index 0000000..5e1aa1d --- /dev/null +++ b/Оглавление.txt @@ -0,0 +1,51 @@ +ВНИМАНИЕ! В этой версии оглавления обнаружены существенные недостатки, потому оно точно изменится в дальнейшем. + +1. Введение + 1.1. Цели и мотивация + 1.2. Терминология + 1.3. Пример работы в виде схемы +2. Параметры + 2.1. Магическое число протокола + 2.2. Версия протокола + 2.3. CryptoDesc [дескриптор криптоалгоритма] + 2.3.1. HashCryptoDesc + 2.3.2. SymCiphCryptoDesc + 2.3.3. AsymCiphCryptoDesc + 2.4. CryptoPack [набор криптоалгоритмов] + 2.4.1. HashCryptoPack + 2.4.2. SymCiphCryptoPack + 2.4.3. SignCryptoPack [набор для подписи, AsymCiphCryptoDesc + HashCryptoDesc] + 2.4.4. KDCryptoPack [набор для Key Derivation, либо существующая KDF, либо SymCiphCryptoDesc + HashCryptoDesc] + 2.4.4.1. KDInitValue [значение, которым впервые инициализируется KDF] + 2.4.4.2. KDPrivConst [приватное значение, используемое в качестве второго аргумента KDF] + 2.6. Linear Binary Map +3. Рукопожатие + 3.1. Базовые параметры + 3.2. Параметры криптографии + 3.3. Запрос + 3.4. Ответ +4. Сессия +5. Поток + 5.1. Состояние + 5.2. SID, SSID, CSID + 5.3. Шифрование + 5.4. Контроль последовательности событий + 5.5. Шум +6. Событие + 6.1. Заголовок + 6.1.1. MAC, CMAC, SMAC [проверка целостности + проверка достоверности] + 6.1.2. Асинхронный идентификатор [AID] + 6.1.3. Тип события + 6.2. Полезная нагрузка + 6.3. Структура + 6.3.1. Открытого события + 6.3.2. Шифрованного события + 6.4. Запрос и ответ +7. Безопасность и модель угроз +8. Распределение типов событий + - ... конкретные типы ... +9. Зарезервированные ячейки LBM + - ... конкретные ячейки ... +10. Расширения протокола + 10.1. Идентификатор расширения [SPXI] + 10.2. Дескриптор расширения [SPXD] diff --git a/Рукопожатие.md b/Рукопожатие.md new file mode 100644 index 0000000..13f77b4 --- /dev/null +++ b/Рукопожатие.md @@ -0,0 +1,236 @@ +# Рукопожатие + +На *рукопожатие* возложена задача создать между *клиентом* и *сервером* новый *поток* на взаимно согласованных условиях. Это включает в себя определение готовности коммуникации по *протоколу*, согласование версии *протокола* в пределах *сессии* (если создаётся новая), а также согласование различных параметров *потока*. *Рукопожатие* может быть "открытое", то есть в котором тело пакета представлено в нешифрованном виде и которое ведёт к созданию нешифрованного *потока*, и "шифрованное", в котором тело пакета зашифровано, подписано, и которое ведёт к созданию шифрованного *потока*. При выполнении этой процедуры используются уникальные форматы пакетов, несоответствующие формальному определению *события*. Формат пакетов использующийся в *рукопожатии* представлен ниже. + +Существуют следующие типы *рукопожатия*: + +- Первый тип: то, что создаёт новый *поток* и одновременно новую *сессию* +- Второй тип: то, что создаёт новый *поток* в рамках существующей *сессии* +- Третий тип: то, что восстанавливает существующий *поток* из десинхронизированного состояния в рабочее + +*Рукопожатие* любого типа выполняется ровно в два действия: + +1. *Клиент* запрашивает *рукопожатие* с избранными параметрами +2. *Сервер* отвечает *клиенту* либо своей частью параметров, либо ошибкой + +Пакет *рукопожатия* состоит из заголовка и тела. В заголовок входят магическое число протокола и флаг шифрования. Тело представляет из себя данные сериализованные в формат LBM. Соответствие ключей ячеек LBM и их содержимого приведено ниже. + +Флаг шифрования является восьмибитным целым числом и может принимать одно из двух значений: + +1. `0x00` - шифрования нет. +2. `0x80` - шифрование включено. + +Формат пакета *рукопожатия* следующий: + +```text +[Magic number: 8 bytes][Encryption flag: 1 byte][Handshake body: non-fixed bytes] +``` + + + +## Базовые параметры + +Базовые параметры, использующиеся в *рукопожатии* и их соответствие ключам ячеек LBM. Обратите внимание, что представленные здесь ключи ячеек не имеют отношения к зарезервированным, которые указаны в разделе 9. + +```text +---------------------------------------------------------------------------------------- +| Name | Key | Description | +|------------------|------|------------------------------------------------------------| +| NoiseParameters | 0x01 | Параметры шума в потоке | +| ProtocolVersion | 0x02 | Версия протокола, использующаяся в сессии | +| StreamSecret | 0x03 | Секрет потока | +| SessionSecret | 0x04 | Секрет сессии | +| MaxPayloadSize | 0x05 | Максимальный размер полезной нагрузки | +| ExtensionsEnum | 0x06 | Перечисление расширений | +| ErrorCode | 0x20 | Код ошибки сервера | +| ErrorDescription | 0x21 | Детальное описание ошибки | +---------------------------------------------------------------------------------------- +``` + +### NoiseParameters + +Является однобайтовым битовым полем, описывает параметры шума в *потоке*. Структура поля выглядит примерно так: + +`[3 bits][2 bits][2 bits][1 bit (reserved)]` + +Первые три бита выделены под настройку количества шумовых *событий*, допустимые значения: + +- `0b000`: отсутствие шумовых *событий* +- `0b001`: соотношение шумовых *событий* к реальным 1:8 +- `0b010`: соотношение 1:4 +- `0b011`: 1:2 +- `0b100`: 1:1 +- `0b101`: 2:1 +- `0b110`: 4:1 +- `0b111`: зарезервировано + +Следующие два бита выделены под настройку размера шумовых *событий*: + +- `0b00`: случайный размер, в пределах от размера минимального *события* с нулевой *полезной нагрузкой*, до MaxPayloadSize +- `0b01`: случайный размер, на основе среднего размера передаваемого *события* +- 25% (но всегда меньше максимального) +- `0b10`: случайный размер, в пределах от минимального, до MaxPayloadSize / 2 +- `0b11`: зарезервировано + +Ещё два бита выделены под настройку размера шумовых данных в обычных *событиях*: + +- `0b00`: без шумовых данных +- `0b01`: заполнение шумовыми данными до лимита *полезной нагрузки* +- `0b10`: заполнение шумовыми данными в случайном количестве, от нуля, до лимита *полезной нагрузки* +- `0b11`: зарезервировано + +Последний бит зарезервирован. + +### StreamSecret + +Секрет *потока* представляет из себя 16 случайных байт, полученные *сервером* с помощью криптостойкого генератора случайных чисел. + +### SessionSecret + +Секрет *сессии* представляет из себя 32 случайных байта, полученные *сервером* с помощью криптостойкого генератора случайных чисел. + +### MaxPayloadSize + +Целое число без знака размером 1, 2, 4 или 8 байт, означающее максимальный размер *полезной нагрузки*. + +### ExtensionsEnum + +Перечисление расширений *протокола* является массивом нефиксированного общего размера и переменным размером отдельной ячейки. В каждой ячейке содержится корректный дескриптор расширения *протокола* (SPXD). + +### ErrorCode + +Код ошибки *сервера* представляет из себя восьмибитное число без знака. Не может являться нулём. Перечень допустимых значений и ассоциированных с ними названий: + +- `0x01`, FailedToParseBody: не удалось корректно разобрать тело пакета +- `0x02`, InsufficientParams: недостаточно параметров для выполнения *рукопожатия* избранного типа +- `0x11`, WrongCryptoAlgo: криптографический алгоритм не указан или не существует +- `0x12`, UnsupportedCryptoAlgo: указанный криптоалгоритм известен *серверу*, но отключен или не имплементирован +- `0x13`, WrongCryptoValue: ключ или иные криптографические данные не указаны, имеют ложный формат или не соответствуют приведённому алгоритму +- `0x14`, FailedToDecryptBody: не удалось расшифровать тело пакета +- `0x21`, InvalidStreamSecret: указанный секрет *потока* не существует +- `0x22`, InvalidSessionSecret: указанный секрет *сессии* не существует +- `0x23`, UnsupportedProtocolVersion: указанная версия *протокола* не поддерживается +- `0x24`, WrongNoiseParams: параметры шума не указаны, имеют ложный формат или отвергнуты *сервером* + +### ErrorDescription + +Строка в кодировке ASCII с текстовым описанием ошибки, без завершающего нулевого байта. + + + +## Криптографические параметры + +Тоже самое, что и базовые параметры, только связанное с криптографией. + +```text +---------------------------------------------------------------------------------------------------- +| Name | Key | Type | Description | +|------------------|------|---------------------|--------------------------------------------------| +| PayloadHashAlgo | 0x40 | HashCryptoPack | Способ хэширования события | # Event signing +| EvSymCiphAlgo | 0x41 | SymCiphCryptoPack | Способ шифрования события | # Event encryption +| EvKDAlgo | 0x42 | KDCryptoPack | Способ генерации ключей шифрования для событий | +| EvKDInitValue | 0x43 | KDInitValue | Значение для инициализации генератора ключей | +| EvKDPrivConst | 0x44 | KDPrivConst | Константа для использования генератором ключей | +| ServerSigAlgo | 0x51 | AsymCiphCryptoDesc | Дескриптор алгоритма подписи сервера | # Server-side event signing +| ServerSigPubKey | 0x52 | | Публичный ключ подписи сервера | +| ClientSigAlgo | 0x55 | AsymCiphCryptoDesc | Дескриптор алгоритма подписи клиента | # Client-side event signing +| ClientSigPubKey | 0x56 | | Публичный ключ подписи клиента | +| SsidKDAlgo | 0x61 | KDCryptoPack | Способ генерации SSID | # Server-side SID setup +| SsidKDInitValue | 0x62 | KDInitValue | Значение для инициализации генератора SSID | +| SsidKDPrivConst | 0x63 | KDPrivConst | Константа для использования генератором SSID | +| CsidKDAlgo | 0x65 | KDCryptoPack | Способ генерации CSID | # Client-side SID setup +| CsidKDInitValue | 0x66 | KDInitValue | Значение для инициализации генератора CSID | +| CsidKDPrivConst | 0x67 | KDPrivConst | Константа для использования генератором CSID | +---------------------------------------------------------------------------------------------------- +``` + + + + + +## 3.3. Запрос + +*Клиент* может отправлять запрос *рукопожатия* второго типа только при условии существования *сессии* между ним и *сервером*, а третьего типа - лишь при условии, что в теле запроса указан существующий *поток* в десинхронизированном состоянии. + +В теле любого пакета-запроса *рукопожатия* должны быть указаны NoiseParameters и может быть указано ExtensionsEnum, содержащее перечень поддерживаемых *клиентом* расширений *протокола*. В теле пакета-запроса *рукопожатия* первого типа должна быть ProtocolVersion, в формате, указанном в разделе 2.2.. В теле пакета-запроса *рукопожатия* второго типа должен содержаться SessionSecret, к которой относится создаваемый *поток*. В теле пакета-запроса *рукопожатия* третьего типа должен быть указан StreamSecret, состояние которого предполагается восстанавливать. + +В шифрованном *рукопожатии*, тело запроса шифруется с помощью заведомо-известного *клиенту* публичного ключа *сервера*. Задаваемые криптографические алгоритмы и ключи, передаваемые *клиентом* в шифрованном запросе *рукопожатия* - распространяются исключительно на создаваемый или восстанавливаемый *поток*. + +В запросе шифрованного *рукопожатия* любого типа должны быть следующие параметры: + +- EvKDInitValue +- SsidKDAlgo +- SsidKDPrivConst +- CsidKDInitValue + +В запросах шифрованного *рукопожатия* первого и второго типов должны быть следующие параметры: + +- PayloadHashAlgo +- EvSymCiphAlgo +- EvKDAlgo +- EvKDPrivConst +- ClientSigAlgo +- ClientSigPubKey + +Также в *рукопожатии* первого и второго типов *клиент* может опционально добавить MaxPayloadSize. + + + +## 3.4. Ответ + +*Сервер* должен отвечать только в случае, если полученные данные имеют корректную структуру, в них присутствует магическое число протокола и корректное значение флага шифрования. + +Если *сервер* принимает запрос *клиента* и отвечает своей частью параметров, то флаг шифрования должен быть установлен в то-же значение, что и в запросе *клиента* и, соответственно флагу, ответ должен быть либо "открытым", либо "шифрованным". Если *сервер* отвечает ошибкой, то, по умолчанию, предполагается "открытый" ответ, за исключением особо-оговорённых случаев. + +При ответе *клиенту* ошибкой, *сервер* может, но не обязан, добавлять в ответ её описание (ErrorDescription). *Клиент* может, но не обязан, обработать это описание наиболее подходящим образом (например - вывести на экран). + +*Сервер* определяет тип *рукопожатия* по запросу, последовательно выполняя следующие шаги: + +1. Проверить, есть-ли в теле запроса секрет *сессии* (SessionSecret). Если да, то это *рукопожатие* второго типа, создание нового *потока*. +2. Проверить, указана-ли в теле версия *протокола* (ProtocolVersion). Если да, то это *рукопожатие* первого типа, создание новой *сессии*. +3. Если проверки на прошлых шагах провалились, то полученный запрос интерпретируется как *рукопожатие* третьего типа, восстановление состояния *потока*. + +*Сервер* отвечает ошибкой... + +- ...FailedToDecryptBody, если проваливает попытку расшифровать тело запроса шифрованного *рукопожатия*. +- ...FailedToParseBody, если проваливает попытку корректно разобрать тело запроса *рукопожатия*. +- ...InsufficientParams, если обнаруживает: + - Отсутствие StreamSecret в *рукопожатии* третьего типа + - Наличие хотя бы одного криптографического параметра, но при этом в целом криптопараметров недостаточно для создания *потока* +- ...UnsupportedProtocolVersion, если не поддерживает запрошенную версию *протокола*. +- ...InvalidStreamSecret, если указанный StreamSecret не существует. +- ...InvalidSessionSecret, если указанный SessionSecret не существует. +- ...UnsupportedCryptoAlgo, если знает, но не поддерживает хотя бы один из параметров: + - PayloadHashAlgo + - EvSymCiphAlgo + - EvKDAlgo + - ClientSigAlgo + - SsidKDAlgo +- ...WrongCryptoAlgo, если обнаруживает недопустимые/неизвестные значения в хотя бы одном из параметров: + - PayloadHashAlgo + - EvSymCiphAlgo + - EvKDAlgo + - ClientSigAlgo + - SsidKDAlgo +- ...WrongCryptoValue, если обнаруживает отсутствие или несоответствие избранным криптографическим параметрам хотя бы одного из параметров: + - EvKDInitValue + - EvKDPrivConst + - CsidKDInitValue + - SsidKDPrivConst + - ClientSigPubKey + +Если после произведения *сервером* всех проверок не обнаружилось каких-либо проблем, то он должен ответить своей частью параметров. Если в *рукопожатии* любого типа *клиент* предоставил ExtensionsEnum, то *сервер* обязан в ответе предоставить ExtensionsEnum с перечислением поддерживаемых им расширений *протокола*. Если в запросе *рукопожатия* содержался параметр MaxPayloadSize, то ответ *сервера* должен содержать MaxPayloadSize со значением равным значению из запроса или меньше. Некорректные значения MaxPayloadSize в запросе - игнорируются. + +В *рукопожатии* первого и второго типа в ответе *сервера* должны содержаться MaxPayloadSize и StreamSecret. В ответе на *рукопожатие* первого типа должен содержаться параметр SessionSecret. + +В ответе шифрованного *рукопожатия* любого типа должны быть параметры: + +- SsidKDInitValue +- CsidKDAlgo +- CsidKDPrivConst + +В ответе шифрованного *рукопожатия* первого типа должны быть параметры: + +- ServerSigAlgo +- ServerSigPubKey + +