diff --git a/.gitignore b/.gitignore index 2a86e25..cf39258 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ _old/* schemes/ -Терминология.md diff --git a/README.md b/README.md index ec4fade..5178454 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Неформальная спецификация протокола Stadium версии 1.0 -Stadium это протокол для безопасной коммуникации общего назначения, работающий поверх обширного перечня транспорта и архитектурно не зависящий от оного. Данная спецификация описывает базу, на основе которой могут быть реализованы расширения для более конкретных нужд. +Stadium это протокол для безопасной коммуникации общего назначения, работающий поверх обширного перечня транспорта и архитектурно не зависящий от оного. Основной фокус при работе над этим проектом идёт на: @@ -10,4 +10,4 @@ Stadium это протокол для безопасной коммуникац - Гибкое и кастомизируемое шифрование; - Расширяемость и возможность подгонки под конкретные задачи. -**Внимание!** В данный момент проект находится в стадии активного проектирования. Данная версия спецификации акцентирует внимание на закреплении общих, преимущественно фундаментальных идей и концепций, потому и является *неформальной*. +**Внимание!** В данный момент проект находится в стадии активного проектирования. Данная версия спецификации абстрактно описывает общие, преимущественно фундаментальные идеи и концепции, не вдаваясь в детали и местами может противоречить конечному результату, потому и является *неформальной*. diff --git a/SPX.md b/SPX.md deleted file mode 100644 index caa20f3..0000000 --- a/SPX.md +++ /dev/null @@ -1,32 +0,0 @@ -# Stadium Protocol eXtension - -Протокол поддерживает различного рода расширения и надстройки, которые могут использоваться в совместимой между собой манере. Под совместимостью подразумевается, что: - -- все корректные пакеты, отправленные узлом без расширения узлу с расширением, будут корректно разобраны и интерпретированы; -- все корректные для базового протокола пакеты, отправленные узлом с расширением узлу без расширения, будут корректно разобраны и интерпретированы; -- расширения не используют конфликтующие элементы функциональности; -- в рамках одной сессии может быть использована любая произвольная комбинация расширений. - -У каждого расширения есть свой уникальный идентификатор/-ы (SPXI) и дескриптор/-ы (SPXD). Идентификатор является либо целым семибитным числом без знака, либо строкой в кодировке ASCII, оканчивающейся нулевым байтом, либо целым числом без знака произвольного размера (1-64 байт). Два последних варианта являются расширенным идентификатором, т.е. e(xtended)SPXI. Обычный идентификатор может использоваться лишь для "официальных" расширений, а расширенный для "официальных" и пользовательских. С расширением может быть ассоциировано вплоть до двух идентификаторов разного типа. Дескриптор расширения предназначен для обмена на этапе рукопожатия и имеет следующую структуру: - -```text -B: byte(s) -b: bit(s) - -|------------------------| -| Extension flag: 1b | -|------------------------| -| eSPXI type or SPXI: 7b | -|------------------------| -| Extended SPXI: ~B | -|------------------------| -``` - -- **Extension flag**: 0 если используется обычный SPXI, 1 если юзается eSPXI. -- **eSPXI type or SPXI**: если флаг 0, то простой идентификатор расширения (SPXI), если флаг 1, то тип расширенного идентификатора (eSPXI). Допустимые типы: - - `0b0000001`: ASCII-строка оканчивающаяся нулём. - - `0b0000010`: целое число произвольного размера. -- **Extended SPXI**: - - Если флаг 0, то отсутствует. - - Если тип eSPXI это строка, то строка, оканчивающаяся нулевым байтом. - - Если тип eSPXI это число, то: `[1 byte: number size in bytes][>0 bytes: number]` diff --git a/Криптография.md b/Криптография.md index 5ee599d..803f30b 100644 --- a/Криптография.md +++ b/Криптография.md @@ -1,8 +1,8 @@ # Криптография -Перед данным протоколом стоит задача обеспечить гибкое шифрование с минимальным ущербом для совместимости между реализациями. Достигается это путём широкого спектра поддерживаемых "из коробки" криптографических алгоритмов и их комбинаций, вкупе с возможностью расширения этого списка в частных случаях, а также возможностью шифрования "в несколько слоёв". Реализуемая стадиумом криптографическая система, помимо базового свойства (сокрытие передаваемых данных от третьих лиц), обеспечивает следующие несколько вещей: +Перед данным протоколом стоит задача обеспечить гибкое шифрование с минимальным ущербом для совместимости между реализациями. Достигается это путём наличия возможности шифрования "в несколько слоёв" и широкого спектра поддерживаемых "из коробки" криптографических алгоритмов и их комбинаций. Реализуемая стадиумом криптографическая система, помимо базового свойства (сокрытие передаваемых данных от третьих лиц), обеспечивает следующие несколько вещей: -- **Достоверность данных**: передаваемые по протоколу данные подписываются с помощью ассиметричного шифрования, алгоритмом, выбранным на этапе подключения, что исключает возможность их подмены. +- **Достоверность данных**: передаваемые по протоколу данные подписываются криптографическим алгоритмом, выбранным на этапе подключения, что исключает возможность их подмены. - **Исключение атак повторного воспроизведения**: каждый пакет снабжён уникальными идентификатором, что приводит к невозможности проведения replay-атак. - **Forward Secrecy**: компрометация долговременного ключа или сессионных ключей не приводит к компрометации прошлых сессионных ключей. - **Future Secrecy**: компрометация сессионных ключей не приводит к компрометации будущих сессионных ключей. @@ -10,11 +10,10 @@ В рамках криптографической системы стадиума существуют несколько понятий, таких как: -- **StreamId**: идентификатор потока. В зашифрованном потоке уникален для каждого отдельного пакета, в незашифрованном - статичен на протяжении всей сессии. Используется для определения принадлежности пакета к потоку данных и, в контексте зашифрованного потока, исключения reply-атак. К одному потоку всегда привязаны ровно два идентификатора, один для использования клиентом и другой для сервера. В случае с зашифрованным потоком, обновляется с помощью KDF, в которой подаётся прошлое значение StreamId и константа. -- **Packet Authentication Code**: код аутентификации пакета. Используется для проверки целостности и достоверности пакета, создаётся путём вычисления хэша из тела события и последующей его подписи с помощью приватного SSK отправителя. Непрошедшие проверку целостности пакеты отбрасываются. -- **Private Stream Signing Key**: сессионный ключ для подписывания пакетов в шифрованном потоке. У сервера и клиента свои ключи. -- **Public Stream Signing Key**: сессионный ключ для верификации пакетов в шифрованном потоке. У сервера и клиента свои ключи. -- **Packet Encryption Key**: сессионный ключ для шифрования содержимого пакетов в шифрованном потоке с помощью симметричного алгоритма. У сервера и клиента свои ключи. В случае с зашифрованным потоком, обновляется с помощью KDF, в которой подаётся прошлое значение PEK и константа. +- **StreamId**: идентификатор потока. В зашифрованном потоке уникален для каждого отдельного пакета, в незашифрованном - статичен на протяжении всей жизни потока. Используется для определения принадлежности пакета к потоку данных и, в контексте зашифрованного потока, исключения reply-атак. К одному потоку всегда привязаны ровно два идентификатора, один для использования клиентом и другой для сервера. В случае с зашифрованным потоком, обновляется с помощью KDF, в которой подаётся прошлое значение StreamId и константа. +- **Packet Authentication Code**: код аутентификации пакета. Используется для проверки целостности и достоверности пакета, создаётся путём вычисления хэша из тела события и последующей его подписи с помощью SSK отправителя. Непрошедшие проверку целостности пакеты отбрасываются. При избыточном количестве пакетов с ложным PAC, сервер имеет права прервать коммуникацию в одностороннем порядке. +- **Stream Signing Key**: сессионный ключ для подписывания пакетов в шифрованном потоке. У сервера и клиента свой ключ. +- **Packet Encryption Key**: сессионный ключ для шифрования содержимого пакетов в шифрованном потоке с помощью симметричного алгоритма. У сервера и клиента свой ключ. Обновляется с помощью KDF, в которой подаётся прошлое значение PEK и константа. - **Private Identity Key**: долговременный приватный ключ. Хранится у сервера и используется для расшифровки полученных запросов рукопожатия. - **Public Identity Key**: долговременный публичный ключ. Ассоциирован с сервером и используется для шифрования запроса рукопожатия при создании шифрованного потока. @@ -38,13 +37,13 @@ Более детальное описание схемы: -1. Алиса отправляет Бобу зашифрованный с помощью публичного IK запрос рукопожатия с целью создания новой сессии и шифрованного потока, содержащий публичный SSK Алисы; инициализационное значение и константу для KDF, отвечающей за обновление PEK Алисы; инициализационное значение и константу для KDF, генерирующего StreamId Алисы; а также прочие параметры создаваемых сессии и потока. -2. Боб принимает шифрованный запрос рукопожатия, расшифровывает с помощью своего приватного IK и отвечает Алисе своим публичным SSK; инициализационным значением и константой KDF, отвечающей за обновление PEK Боба; инициализационное значение и константу для KDF, генерирующего StreamId Боба; а также прочей своей частью параметров, зашифровав их с помощью публичного SSK Алисы. -3. Алиса шифрует пакет с помощью своего PEK, подписывает с помощью своего приватного SSK, снабжает текущим StreamId и отправляет Бобу. -4. Боб получает пакет, проверяет его с помощью публичного SSK Алисы и расшифровывает с помощью PEK Алисы. После этого Алиса и Боб обновляют PEK и StreamId Алисы с помощью двух KDF. -5. Боб шифрует пакет с помощью своего PEK, подписывает с помощью своего приватного SSK, снабжает текущим StreamId и отправляет Алисе. -6. Алиса получает пакет, проверяет его с помощью публичного SSK Боба и расшифровывает с помощью PEK Боба. После этого Боб и Алиса обновляют PEK и StreamId Боба с помощью двух KDF. +1. Алиса отправляет Бобу зашифрованный с помощью публичного IK запрос рукопожатия с целью создания новой сессии и шифрованного потока, содержащий SSK Алисы; инициализационное значение и константу для KDF, отвечающей за обновление PEK Алисы; инициализационное значение и константу для KDF, генерирующего StreamId Алисы; а также прочие параметры создаваемых сессии и потока. +2. Боб принимает шифрованный запрос рукопожатия, расшифровывает с помощью своего приватного IK и отвечает Алисе своим SSK; инициализационным значением и константой KDF, отвечающей за обновление PEK Боба; инициализационное значение и константу для KDF, генерирующего StreamId Боба; а также прочей своей частью параметров, зашифровав их с помощью SSK Алисы. +3. Алиса шифрует пакет с помощью своего PEK, подписывает с помощью своего SSK, снабжает текущим StreamId и отправляет Бобу. +4. Боб получает пакет, проверяет его с помощью SSK Алисы и расшифровывает с помощью PEK Алисы. После этого Алиса и Боб обновляют PEK и StreamId Алисы с помощью двух KDF. +5. Боб шифрует пакет с помощью своего PEK, подписывает с помощью своего SSK, снабжает текущим StreamId и отправляет Алисе. +6. Алиса получает пакет, проверяет его с помощью SSK Боба и расшифровывает с помощью PEK Боба. После этого Боб и Алиса обновляют PEK и StreamId Боба с помощью двух KDF. 7. Алиса запрашивает обновление константы KDF, генерирующей её PEK, помещая в пакет новое значение константы, после чего повторяя шаги из пункта 3. -8. Боб получает пакет, проверяет его с помощью публичного SSK Алисы и расшифровывает с помощью PEK Алисы. Затем Алиса и Боб обновляют константу для KDF генерирующей PEK Алисы, а также сам PEK и StreamId Алисы с помощью двух KDF. +8. Боб получает пакет, проверяет его с помощью SSK Алисы и расшифровывает с помощью PEK Алисы. Затем Алиса и Боб обновляют константу для KDF генерирующей PEK Алисы, а также сам PEK и StreamId Алисы с помощью двух KDF. 9. Алиса запрашивает обновление константы KDF, генерирующей её StreamId, помещая в пакет новое значение константы, после чего повторяя шаги из пункта 3. 10. Боб повторяет шаги из пункта 8, но вместо обновления константы KDF для генерации PEK - обновляет уже константу для генерации StreamId Алисы. diff --git a/Оглавление.txt b/Оглавление.txt index 11a1408..49caaac 100644 --- a/Оглавление.txt +++ b/Оглавление.txt @@ -1,64 +1,8 @@ -1. Введение - 1.1. Безопасность и модель угроз -2. Базовая терминология -3. Криптография - 3.1. Дескриптор криптоалгоритма [CryptoDs] - 3.1.1. Дескриптор хэш-функции [HashCryptoDs] - 3.1.2. Дескриптор симметричного шифра [SymCiphCryptoDs] - 3.1.3. Дескриптор асимметричного шифра [AsymCiphCryptoDs] - 3.2. Дескриптор набора криптоалгоритмов [CryptoPack] - 3.2.1. Набор для хэширования [HashCryptoPack] - 3.2.2. Набор для симметричного шифрования [SymCiphCryptoPack] - 3.2.3. Набор для подписи [SignCryptoPack; набор для подписи, т.е. AsymCiphCryptoDs + HashCryptoDs] - 3.2.4. Набор для генерации ключей [KDCryptoPack; набор для Key Derivation, т.е. существующая KDF или SymCiphCryptoDs + HashCryptoDs] - 3.2.4.1. Стартовое значение для KDF [KDInitValue; значение, которым впервые инициализируется KDF] - 3.2.4.2. Константа для KDF [KDPrivConst; приватное значение, используемое в качестве второго аргумента KDF] -4. Установка соединения - 4.1. Возможные параметры - 4.1.1. Магическое число протокола - 4.1.2. Версия протокола - 4.2. Запрос рукопожатия - 4.3. Ответ на рукопожатие -5. Сессия - 5.1. Поток - 5.1.1. Состояние [StreamState; Running, Desynchronized] - 5.1.2. Идентификатор потока [StreamId, SrvStreamId, CltStreamId] - 5.1.3. Секрет потока [StreamSecret] - 5.1.4. Криптографические параметры потока - 5.1.5. Параметры сжатия потока - 5.1.6. Контроль последовательности - 5.1.7. Шумовые пакеты - 5.2. Секрет сессии [SessionSecret] -6. Событие - 6.1. Тип события - 6.2. Зарезервированные типы и диапазоны типов событий - 6.3. Содержание события - 6.3.1. Формат содержания зарезервированных событий - 6.4. Запрос и ответ [абстрактно] -7. Пакет - 7.1. Заголовок пакета - 7.1.1. Код аутентификации [Packet Authentication Code, PAC, CltPAC, SrvPAC; проверка целостности и достоверности] - 7.1.2. Флаги [сжатие, наличие шума, наличие идентификатора] - 7.1.3. Идентификатор [PacketID; присутствует только если включены подтверждения доставки или требуется ответ] - 7.2. Полезная нагрузка - 7.2.1. Шум в полезной нагрузке пакета - 7.3. Структура и шифрование - 7.4. Пачка пакетов и разбиение полезной нагрузки -8. Транспортный адаптер - 8.1. Дескриптор транспортного адаптера [TransportDs] - 8.2. Свойства адаптеров - 8.2.1. Максимальный размер полезной нагрузки - 8.2.2. Гарантия последовательности - 8.2.3. Гарантия обнаружения недоставки [?] - 8.2.4. Гарантия целостности - 8.2.5. Наличие шифрования - 8.3. Список адаптеров [мб стоит вынести в отдельную спеку] - 8.3.1. UDP-raw - 8.3.2. TCP-raw - 8.3.3. HTTP1.1-text+binary [текстовые HTTP/1.1 POST-запросы и бинарные ответы] - 8.3.4. [???] -9. Сериализация данных [Linear Binary Map (?)] - 9.1. Используемые ключи полезной нагрузки -10. Расширения протокола [SPX] - 10.1. Идентификатор расширения [SPXI] - 10.2. Дескриптор расширения [SPXD] +1. Криптография +2. Рукопожатие +3. Сессия +4. Поток +5. Пакет +6. Системное событие +7. Транспортный адаптер +8. Сериализация данных и формат LBM diff --git a/Пакет.md b/Пакет.md index 68a3923..9e7eade 100644 --- a/Пакет.md +++ b/Пакет.md @@ -2,9 +2,9 @@ Пакет это физическая единица информации протокола стадиум. Пакет состоит из идентификатора потока, заголовка и тела. Применимость шифрования как такового, криптографические алгоритмы и алгоритмы сжатия - определяются потоком, по которому передаётся пакет. В шифрованном потоке, заголовок шифруется вместе с телом. -В заголовке пакета находятся такие данные как: код аутентификации пакета (Packet Authentication Code), который представляет из себя хэш от обработанных данных и если пакет передаётся по шифрованному потоку, то он дополнительно подписан; флаги наличия сжатия, шумовых данных и идентификатора; и идентификатор пакета, который присутствует лишь в случае, если в потоке включено подтверждение доставки и/или к данному пакету предполагается ответ. +В заголовке пакета находятся такие данные как: код аутентификации пакета (Packet Authentication Code), который представляет из себя хэш от обработанных данных и если пакет передаётся по шифрованному потоку, то он дополнительно подписан; флаги наличия сжатия, шумовых данных, идентификатора и флаг системного события; и идентификатор пакета, который присутствует лишь в случае, если в потоке включено подтверждение доставки и/или к данному пакету предполагается ответ. -В теле пакета содержится произвольное событие и шум (если включен на уровне потока). Ограничения на максимальный размер тела пакета протокола стадиум нет, но оно может опционально подстраиваться под максимальный размер пакета протокола транспортного уровня. В таком случае, если событие не помещается в лимит транспорта - оно разбивается на несколько стадиумных пакетов, которые представляют из себя пачку пакетов. +В теле пакета содержутся произвольные данные и шум (если включен на уровне потока). Ограничения на максимальный размер тела пакета протокола стадиум нет, но оно может быть опционально задано на этапе рукопожатия и/или подстраиваться под максимальный размер пакета протокола транспортного уровня. В таком случае, если данные не помещаются в лимит транспорта - они разбивается на несколько стадиумных пакетов, которые представляют из себя пачку пакетов. При попытке отправки приложением данных, превышающих лимит размера тела пакета, на стороне отправляющего узла должна возникнуть ошибка. Узел-получатель не должен предполагать, что получает данные валидной длинны и должен производить проверки самостоятельно. ## Ориентировочная схема структуры пакета diff --git a/Поток.md b/Поток.md index c44b505..b21dc36 100644 --- a/Поток.md +++ b/Поток.md @@ -1,6 +1,6 @@ # Поток -Поток - это логический канал для передачи пакетов, существующий в пределах конкретной сессии. Потоки могут создаваться и уничтожаться по инициативе клиента, сервер может только ограничивать создание новых потоков или уничтожать потоки исходя из соображений защиты от DoS-атак. При создании сессии всегда создаётся как минимум один поток. Клиенты могут создавать потоки только в пределах собственной сессии. +Поток - это логический канал для передачи пакетов, существующий в пределах конкретной сессии. Потоки создаются и уничтожаются по инициативе клиента. Сервер может ограничивать создание новых потоков и в нормальных условиях не должен уничтожать потоки. При создании сессии всегда создаётся один поток. Клиенты могут создавать потоки только в пределах собственной сессии. У потока есть некий набор параметров, часть из которых он разделяет с сессией. У каждого потока есть свой уникальный идентификатор (StreamId) и секрет (StreamSecret), последний задаётся на этапе создания нового потока. Поток может быть как "шифрованным", так и "нешифрованным", что влияет на формат пакетов: в нешифрованном потоке, идентификатор потока статичен, а в шифрованном - обновляется и уникален для каждого пакета. Шифрованный поток может находиться в одном из двух состояний: "работает" или "рассинхронизирован", а нешифрованный только в первом. В случае непредвиденного нарушения работоспособности шифрованного потока, он переходит в состояние "рассинхронизирован", а вернуть его обратно в состояние "работает" клиент может путём отправки серверу пакета со специальным видом рукопожатия, содержащим StreamSecret. diff --git a/Рукопожатие.md b/Рукопожатие.md index 6e35a04..d90c7c8 100644 --- a/Рукопожатие.md +++ b/Рукопожатие.md @@ -1,238 +1,68 @@ # Рукопожатие -**ВНИМАНИЕ!** _Стиль данного файла не вполне соответствует статусу "неформальной" версии спецификации и слишком заостряет внимание на деталях реализации. Следует иметь ввиду, что в конечном счёте всё может и будет изменено._ +На рукопожатие возложена задача создать между клиентом и сервером новый поток или восстановить существующий (из десинхронизированного состояния в рабочее), на взаимно согласованных условиях. Это включает в себя определение готовности коммуникации по протоколу, согласование версии протокола в пределах сессии (если создаётся новая), а также согласование различных параметров потока. Рукопожатие может быть "открытое", то есть в котором тело пакета представлено в нешифрованном виде и которое ведёт к созданию нешифрованного потока, и "шифрованное", в котором тело пакета зашифровано, подписано, и которое ведёт к созданию шифрованного потока. При выполнении этой процедуры используются данные в уникальном формате, несоответствующие формальному определению пакета. Формат данных использующийся в рукопожатии представлен ниже. -На *рукопожатие* возложена задача создать между *клиентом* и *сервером* новый *поток* на взаимно согласованных условиях. Это включает в себя определение готовности коммуникации по *протоколу*, согласование версии *протокола* в пределах *сессии* (если создаётся новая), а также согласование различных параметров *потока*. *Рукопожатие* может быть "открытое", то есть в котором тело пакета представлено в нешифрованном виде и которое ведёт к созданию нешифрованного *потока*, и "шифрованное", в котором тело пакета зашифровано, подписано, и которое ведёт к созданию шифрованного *потока*. При выполнении этой процедуры используются уникальные форматы пакетов, несоответствующие формальному определению *события*. Формат пакетов использующийся в *рукопожатии* представлен ниже. +Существуют следующие типы рукопожатия: -Существуют следующие типы *рукопожатия*: +- Первый тип: создаёт новую сессию +- Второй тип: создаёт новый поток в рамках существующей сессии +- Третий тип: восстанавливает существующий поток из десинхронизированного состояния в рабочее -- Первый тип: то, что создаёт новый *поток* и одновременно новую *сессию* -- Второй тип: то, что создаёт новый *поток* в рамках существующей *сессии* -- Третий тип: то, что восстанавливает существующий *поток* из десинхронизированного состояния в рабочее +Рукопожатие любого типа выполняется ровно в два действия: -*Рукопожатие* любого типа выполняется ровно в два действия: +1. Клиент запрашивает рукопожатие с избранными параметрами +2. Сервер отвечает клиенту либо своей частью параметров, либо ошибкой -1. *Клиент* запрашивает *рукопожатие* с избранными параметрами -2. *Сервер* отвечает *клиенту* либо своей частью параметров, либо ошибкой - -Пакет *рукопожатия* состоит из заголовка и тела. В заголовок входят магическое число протокола и флаг шифрования. Тело представляет из себя данные сериализованные в формат LBM. Соответствие ключей ячеек LBM и их содержимого приведено ниже. +Пакет рукопожатия состоит из заголовка и тела. В заголовок входят магическое число протокола и флаг шифрования. Тело представляет из себя данные сериализованные в формат LBM. Флаг шифрования является восьмибитным целым числом и может принимать одно из двух значений: 1. `0x00` - шифрования нет. 2. `0x80` - шифрование включено. -Формат пакета *рукопожатия* следующий: +Формат пакета рукопожатия следующий: ```text -[Magic number: 8 bytes][Encryption flag: 1 byte][Handshake body: non-fixed bytes] -``` +B: byte(s) +|----------------------| +| Magic number: 8B | +|----------------------| +| Encryption flag: 1B | +|----------------------| +| Handshake body: >=1B | +|----------------------| +``` ## Базовые параметры -Базовые параметры, использующиеся в *рукопожатии* и их соответствие ключам ячеек 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 - - +- Алгоритм хэширования пакета +- Алгоритм шифрования события +- KDF для генерации ключей шифрования для пакетов +- Значение для инициализации KDF ключей пакетов +- Константа для использования KDF ключей пакетов +- Алгоритм подписи сервера +- Публичный ключ подписи сервера +- Алгоритм подписи клиента +- Публичный ключ подписи клиента +- KDF для генерации Server StreamId +- Значение для инициализации KDF SSId +- Константа для KDF SSId +- KDF для генерации Client StreamId +- Значение для инициализации KDF CSId +- Константа для KDF CSId diff --git a/Сериализация (LBM).md b/Сериализация (LBM).md index 4518381..8daccfb 100644 --- a/Сериализация (LBM).md +++ b/Сериализация (LBM).md @@ -1,8 +1,8 @@ # Сериализация и формат LBM -Данный формат сериализации используются в теле событий зарезервированных типов. Данные сериализованные в формат *LBM* (AKA "данные в формате *LBM*") являются расположенными последовательно ячейками с ключом, длинной значения и значением, в неопределённом порядке относительно друг-друга. Положение ячеек относительно друг-друга не детерминированно и они могут быть намеренно перемешаны. Отсутствие каких-либо ячеек вообще обозначается единичным нулевым байтом. +Данный формат сериализации используются в теле системных событий и рукопожатии. Данные сериализованные в формат LBM (AKA "данные в формате LBM") являются расположенными последовательно ячейками с ключом, длинной значения и значением, в неопределённом порядке относительно друг-друга. Положение ячеек относительно друг-друга не детерминированно и они могут быть намеренно перемешаны. Отсутствие каких-либо ячеек вообще обозначается единичным нулевым байтом. -Ключ является однобайтным числом без знака. Ключ всегда больше нуля. В контексте протокола и событий зарезервированного типа, некоторые ключи имеют особое значение. Длинна значения является двухбайтным числом без знака. Если ячейка пуста, то длинна нулевая. Значением является произвольная последовательность байт. +Ключ является однобайтным числом без знака. Ключ всегда больше нуля. В каждом контексте (контекст системных событий и контекст рукопожатия) есть свой набор выделенных ключей. Все остальные, неиспользуемые ключи при парсинге опускаются. Длинна значения является двухбайтным числом без знака. Если ячейка пуста, то длинна нулевая. Значением является произвольная последовательность байт. Одна ячейка имеет следующий вид: diff --git a/Сессия.md b/Сессия.md index a00e484..a46c598 100644 --- a/Сессия.md +++ b/Сессия.md @@ -1,3 +1,3 @@ # Сессия -Сессия является связующим звеном для потоков, созданных одним узлом. У каждой сессии есть свой набор параметров, задаваемый в момент её создания. Этот набор включает в себя: используемую версия протокола, набор расширений и секрет сессии (SessionSecret), нужный для создания новых потоков в пределах текущей сессии. Сессия создаётся при первом рукопожатии клиента с сервером. При уничтожении сессии - уничтожаются все ассоциированные с ней потоки. +Сессия является связующим звеном для потоков, созданных одним узлом. Пакет, полученный из любого потока, принадлежащего одной сессии интерпретируется как пакет от одного и того-же клиента. У каждой сессии есть свой набор параметров, задаваемый в момент её создания. Этот набор включает в себя используемую версию протокола. Сессия создаётся при первом рукопожатии клиента с сервером. При уничтожении сессии - уничтожаются все ассоциированные с ней потоки. diff --git a/Системное событие.md b/Системное событие.md new file mode 100644 index 0000000..b617df4 --- /dev/null +++ b/Системное событие.md @@ -0,0 +1,17 @@ +# Системное событие + +Системное событие представляет из себя смысловую единицу в определённом формате, помещаемую в пакеты с установленным флагом системного события и предназначенную для управления соединением. Событие состоит из типа и тела. Тип описывает способ интерпретации тела, а тело содержит данные в формате LBM. + +Тип события является однобайтным целым числом без знака. Событие может предполагать ответ от противоположного узла или не требовать оный. Наличие необходимости ответа определяется типом, в каждом случае индивидуально. Корректный тип события никогда не равен нулю. + +Структура события может быть представлена следующим образом: + +```text +B: byte(s) + +|----------| +| Type: 1B | +|----------| +| Body: ~B | +|----------| +``` diff --git a/Событие.md b/Событие.md deleted file mode 100644 index be3dbf9..0000000 --- a/Событие.md +++ /dev/null @@ -1,5 +0,0 @@ -# Событие - -Событие представляет из себя минимальную смысловую единицу, которой могут обмениваться узлы в соединении. Событиями из зарезервированного диапазона типов оперирует протокол, с целью управления соединением, все остальные события могут использоваться уровнем приложения для прочих нужд. Событие содержит тип и тело. Тип описывает способ интерпретации тела, а тело содержит произвольные данные, размер которых ограничен значением, обозначенным на этапе хэндшейка. При попытке отправки приложением данных, превышающих этот лимит, на стороне отправляющего узла должна возникнуть ошибка. Узел-получатель не должен предполагать, что получает данные валидной длинны и должен производить проверки самостоятельно. - -Тип события является двухбайтным целым числом без знака. Событие может предполагать ответ от противоположного узла или не требовать оный. Наличие необходимости ответа определяется типом, в каждом случае индивидуально. Корректный тип события никогда не равен нулю. diff --git a/Транспортный адаптер.md b/Транспортный адаптер.md index 3157ff5..3904478 100644 --- a/Транспортный адаптер.md +++ b/Транспортный адаптер.md @@ -1,3 +1,11 @@ # Транспортный адаптер -Адаптер представляет из себя нечто, способное вести коммуникацию в рамках конкретного транспортного протокола, преобразовывать передаваемый ему произвольный набор байт в форму, приемлимую транспортным протоколом и корректно воспринимаемую другим адаптером того-же вида, а также выполнять обратное преобразование. В рамках базового протокола определены лишь несколько адаптеров, предназначенные для использования в качестве транспорта протоколы UDP, TCP, HTTP/1.1 и HTTP/1.1+TLS1.3. +Адаптер представляет из себя нечто, способное вести коммуникацию в рамках конкретного транспортного протокола, преобразовывать передаваемый ему произвольный набор байт в форму, приемлимую транспортным протоколом и корректно воспринимаемую другим адаптером того-же вида, а также выполнять обратное преобразование. + +В рамках базового протокола предписана реализация нескольких адаптеров, для протоколов: + +- UDP +- TCP +- HTTP/1.1 (RFC 2616) +- HTTP/1.1 + TLS 1.3 +- SSH Transport Layer Protocol (RFC 4253)