Начало разработки полной версии спецификации
Практически исчерпывающе описано понятие сессии.
This commit is contained in:
parent
08001d7aaa
commit
550661e71f
11
README.md
11
README.md
@ -1,13 +1,14 @@
|
||||
# Неформальная спецификация протокола Stadium версии 1.0
|
||||
# Спецификация протокола Stadium версии 1.0
|
||||
|
||||
Stadium это протокол для безопасной коммуникации общего назначения, работающий поверх обширного перечня транспорта и архитектурно не зависящий от оного. Данная спецификация описывает базу, на основе которой могут быть реализованы расширения для более конкретных нужд. <!--Помимо прочего, данный протокол служит основой для полнофункционального мессенджера Marafon, спецификация расширения которого находится в папке `SPX/Marafon/`.-->
|
||||
Stadium это протокол для безопасной коммуникации общего назначения, ведомый целью унифицировать обмен данными по разным транспортным протоколам в гибкой и безопасной манере. Данная спецификация описывает базу, на основе которой могут быть реализованы расширения для более конкретных нужд.
|
||||
|
||||
Основной фокус при работе над этим проектом идёт на:
|
||||
|
||||
- Минимизацию оверхэда и возможность функционирования в условиях низкой пропускной способности канала;
|
||||
- Возможность функционирования при низкой пропускной способности канала;
|
||||
- Приемлимое функционирование в условиях больших потерь пакетов;
|
||||
- Минимизацию оверхэда;
|
||||
- Поддержку широкого спектра транспортных протоколов "из коробки";
|
||||
- Гибкое и кастомизируемое шифрование;
|
||||
- Расширяемость и возможность подгонки под конкретные задачи.
|
||||
- Совместимость между разными версиями ПО.
|
||||
|
||||
**Внимание!** В данный момент проект находится в стадии активного проектирования. Данная версия спецификации акцентирует внимание на закреплении общих, преимущественно фундаментальных идей и концепций, потому и является *неформальной*.
|
||||
**Внимание!** В данный момент проект находится в стадии активного проектирования.
|
||||
|
32
SPX.md
32
SPX.md
@ -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]`
|
@ -1,50 +0,0 @@
|
||||
# Криптография
|
||||
|
||||
Перед данным протоколом стоит задача обеспечить гибкое шифрование с минимальным ущербом для совместимости между реализациями. Достигается это путём широкого спектра поддерживаемых "из коробки" криптографических алгоритмов и их комбинаций, вкупе с возможностью расширения этого списка в частных случаях, а также возможностью шифрования "в несколько слоёв". Реализуемая стадиумом криптографическая система, помимо базового свойства (сокрытие передаваемых данных от третьих лиц), обеспечивает следующие несколько вещей:
|
||||
|
||||
- **Достоверность данных**: передаваемые по протоколу данные подписываются с помощью ассиметричного шифрования, алгоритмом, выбранным на этапе подключения, что исключает возможность их подмены.
|
||||
- **Исключение атак повторного воспроизведения**: каждый пакет снабжён уникальными идентификатором, что приводит к невозможности проведения replay-атак.
|
||||
- **Forward Secrecy**: компрометация долговременного ключа или сессионных ключей не приводит к компрометации прошлых сессионных ключей.
|
||||
- **Future Secrecy**: компрометация сессионных ключей не приводит к компрометации будущих сессионных ключей.
|
||||
- **Исключение различных вариаций статистического анализа**: сессионные ключи регулярно проходят ротацию, в соединение подмешиваются "шумовые" пакеты, а в пакеты с реальными данными подмешиваются "шумовые данные", что размывает статистические характеристики и затрудняет анализ, предотвращая разные формы атак на основе шифротекста.
|
||||
|
||||
В рамках криптографической системы стадиума существуют несколько понятий, таких как:
|
||||
|
||||
- **StreamId**: идентификатор потока. В зашифрованном потоке уникален для каждого отдельного пакета, в незашифрованном - статичен на протяжении всей сессии. Используется для определения принадлежности пакета к потоку данных и, в контексте зашифрованного потока, исключения reply-атак. К одному потоку всегда привязаны ровно два идентификатора, один для использования клиентом и другой для сервера. В случае с зашифрованным потоком, обновляется с помощью KDF, в которой подаётся прошлое значение StreamId и константа.
|
||||
- **Packet Authentication Code**: код аутентификации пакета. Используется для проверки целостности и достоверности пакета, создаётся путём вычисления хэша из тела события и последующей его подписи с помощью приватного SSK отправителя. Непрошедшие проверку целостности пакеты отбрасываются.
|
||||
- **Private Stream Signing Key**: сессионный ключ для подписывания пакетов в шифрованном потоке. У сервера и клиента свои ключи.
|
||||
- **Public Stream Signing Key**: сессионный ключ для верификации пакетов в шифрованном потоке. У сервера и клиента свои ключи.
|
||||
- **Packet Encryption Key**: сессионный ключ для шифрования содержимого пакетов в шифрованном потоке с помощью симметричного алгоритма. У сервера и клиента свои ключи. В случае с зашифрованным потоком, обновляется с помощью KDF, в которой подаётся прошлое значение PEK и константа.
|
||||
- **Private Identity Key**: долговременный приватный ключ. Хранится у сервера и используется для расшифровки полученных запросов рукопожатия.
|
||||
- **Public Identity Key**: долговременный публичный ключ. Ассоциирован с сервером и используется для шифрования запроса рукопожатия при создании шифрованного потока.
|
||||
|
||||
|
||||
## Пример коммуникации двух узлов
|
||||
|
||||
Ниже приведена схема коммуникации двух узлов (Алиса - клиент, Боб - сервер), при условии, что PEK обновляются каждый пакет (т.е. уникальны для каждого отдельного пакета).
|
||||
|
||||
```text
|
||||
1: Alice -- request handshake --> Bob
|
||||
2: Alice <-- accept handshake -- Bob
|
||||
3: Alice -- send encrypted packet --> Bob
|
||||
4: Alice and Bob updates Alice's PEK and StreamId using KDFs
|
||||
5: Alice <-- send encrypted packet -- Bob
|
||||
6: Bob and Alice updates Bob's PEK and StreamId using KDFs
|
||||
7: Alice -- update PEK KDF const --> Bob
|
||||
8: Alice and Bob updates Alice's PEK using KDF with new const and StreamId
|
||||
9: Alice -- update StreamId KDF const --> Bob
|
||||
10: Alice and Bob updates Alice's PEK and StreamId using KDF with new const
|
||||
```
|
||||
|
||||
Более детальное описание схемы:
|
||||
|
||||
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.
|
||||
9. Алиса запрашивает обновление константы KDF, генерирующей её StreamId, помещая в пакет новое значение константы, после чего повторяя шаги из пункта 3.
|
||||
10. Боб повторяет шаги из пункта 8, но вместо обновления константы KDF для генерации PEK - обновляет уже константу для генерации StreamId Алисы.
|
127
Оглавление.txt
127
Оглавление.txt
@ -1,64 +1,63 @@
|
||||
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]
|
||||
- Введение
|
||||
- Безопасность и модель угроз
|
||||
- Базовая терминология
|
||||
- Криптография
|
||||
- Дескриптор криптоалгоритма [CryptoDs]
|
||||
- Дескриптор хэш-функции [HashCryptoDs]
|
||||
- Дескриптор симметричного шифра [SymCiphCryptoDs]
|
||||
- Дескриптор асимметричного шифра [AsymCiphCryptoDs]
|
||||
- Дескриптор набора криптоалгоритмов [CryptoPack]
|
||||
- Набор для хэширования [HashCryptoPack]
|
||||
- Набор для симметричного шифрования [SymCiphCryptoPack]
|
||||
- Набор для подписи [SignCryptoPack; набор для подписи, т.е. AsymCiphCryptoDs + HashCryptoDs]
|
||||
- Набор для генерации ключей [KDCryptoPack; набор для Key Derivation, т.е. существующая KDF или SymCiphCryptoDs + HashCryptoDs]
|
||||
- Стартовое значение для KDF [KDInitValue; значение, которым впервые инициализируется KDF]
|
||||
- Константа для KDF [KDPrivConst; приватное значение, используемое в качестве второго аргумента KDF]
|
||||
- Установка соединения
|
||||
- Возможные параметры
|
||||
- Магическое число протокола
|
||||
- Версия протокола
|
||||
- Запрос рукопожатия
|
||||
- Ответ на рукопожатие
|
||||
- Сессия
|
||||
- Поток
|
||||
- Состояние [StreamState; Running, Desynchronized]
|
||||
- Идентификатор потока [StreamId, SrvStreamId, CltStreamId]
|
||||
- Секрет потока [StreamSecret]
|
||||
- Криптографические параметры потока
|
||||
- Параметры сжатия потока
|
||||
- Контроль последовательности
|
||||
- Шумовые пакеты
|
||||
- Событие
|
||||
- Тип события
|
||||
- Зарезервированные типы и диапазоны типов событий
|
||||
- Содержание события
|
||||
- Формат содержания зарезервированных событий
|
||||
- Запрос и ответ [абстрактно]
|
||||
- Пакет
|
||||
- Заголовок пакета
|
||||
- Код аутентификации [Packet Authentication Code, PAC, CltPAC, SrvPAC; проверка целостности и достоверности]
|
||||
- Флаги [сжатие, наличие шума, наличие идентификатора]
|
||||
- Идентификатор [PacketID; присутствует только если включены подтверждения доставки или требуется ответ]
|
||||
- Полезная нагрузка
|
||||
- Шум в полезной нагрузке пакета
|
||||
- Структура и шифрование
|
||||
- Пачка пакетов и разбиение полезной нагрузки
|
||||
- Транспортный адаптер
|
||||
- Дескриптор транспортного адаптера [TransportDs]
|
||||
- Свойства адаптеров
|
||||
- Максимальный размер полезной нагрузки
|
||||
- Гарантия последовательности
|
||||
- Гарантия обнаружения недоставки [?]
|
||||
- Гарантия целостности
|
||||
- Наличие шифрования
|
||||
- Список адаптеров [мб стоит вынести в отдельную спеку]
|
||||
- UDP-raw
|
||||
- TCP-raw
|
||||
- HTTP1.1-text+binary [текстовые HTTP/1.1 POST-запросы и бинарные ответы]
|
||||
- [???]
|
||||
- Сериализация данных [Linear Binary Map (?)]
|
||||
- Используемые ключи полезной нагрузки
|
||||
- Расширения протокола [SPX]
|
||||
- Идентификатор расширения [SPXI]
|
||||
- Дескриптор расширения [SPXD]
|
||||
|
26
Пакет.md
26
Пакет.md
@ -1,26 +0,0 @@
|
||||
# Пакет
|
||||
|
||||
Пакет это физическая единица информации протокола стадиум. Пакет состоит из идентификатора потока, заголовка и тела. Применимость шифрования как такового, криптографические алгоритмы и алгоритмы сжатия - определяются потоком, по которому передаётся пакет. В шифрованном потоке, заголовок шифруется вместе с телом.
|
||||
|
||||
В заголовке пакета находятся такие данные как: код аутентификации пакета (Packet Authentication Code), который представляет из себя хэш от обработанных данных и если пакет передаётся по шифрованному потоку, то он дополнительно подписан; флаги наличия сжатия, шумовых данных и идентификатора; и идентификатор пакета, который присутствует лишь в случае, если в потоке включено подтверждение доставки и/или к данному пакету предполагается ответ.
|
||||
|
||||
В теле пакета содержится произвольное событие и шум (если включен на уровне потока). Ограничения на максимальный размер тела пакета протокола стадиум нет, но оно может опционально подстраиваться под максимальный размер пакета протокола транспортного уровня. В таком случае, если событие не помещается в лимит транспорта - оно разбивается на несколько стадиумных пакетов, которые представляют из себя пачку пакетов.
|
||||
|
||||
|
||||
## Ориентировочная схема структуры пакета
|
||||
|
||||
```text
|
||||
B: byte(s)
|
||||
|
||||
|-------------------------|
|
||||
| StreamId: 4B |
|
||||
|-------------------------|
|
||||
| PAC: >=8B |
|
||||
|-------------------------|
|
||||
| Flags: 1B |
|
||||
|-------------------------|
|
||||
| PacketId (optional): 2B |
|
||||
|-------------------------|
|
||||
| Body: >0B |
|
||||
|-------------------------|
|
||||
```
|
9
Поток.md
9
Поток.md
@ -1,9 +0,0 @@
|
||||
# Поток
|
||||
|
||||
Поток - это логический канал для передачи пакетов, существующий в пределах конкретной сессии. Потоки могут создаваться и уничтожаться по инициативе клиента, сервер может только ограничивать создание новых потоков или уничтожать потоки исходя из соображений защиты от DoS-атак. При создании сессии всегда создаётся как минимум один поток. Клиенты могут создавать потоки только в пределах собственной сессии.
|
||||
|
||||
У потока есть некий набор параметров, часть из которых он разделяет с сессией. У каждого потока есть свой уникальный идентификатор (StreamId) и секрет (StreamSecret), последний задаётся на этапе создания нового потока. Поток может быть как "шифрованным", так и "нешифрованным", что влияет на формат пакетов: в нешифрованном потоке, идентификатор потока статичен, а в шифрованном - обновляется и уникален для каждого пакета. Шифрованный поток может находиться в одном из двух состояний: "работает" или "рассинхронизирован", а нешифрованный только в первом. В случае непредвиденного нарушения работоспособности шифрованного потока, он переходит в состояние "рассинхронизирован", а вернуть его обратно в состояние "работает" клиент может путём отправки серверу пакета со специальным видом рукопожатия, содержащим StreamSecret. <!--TODO: мб написать чуть подробнее, что за "непредвиденное нарушение"-->
|
||||
|
||||
Значительная часть функциональности протокола, в том числе касающаяся криптографии, опциональна в том смысле, что может не использоваться в рамках отдельного потока. К примеру, в нём может быть не только настроено количество шума, но и полностью отключен. Таким-же образом, коммуникация может происходить вовсе без шифрования, что может быть полезно, например, для снижения нагрузки на железо, когда протокол транспортного уровня уже обеспечивает требуемый уровень приватности. Поток также может гарантировать последовательность отправки пакетов и информирование в случае недоставки или повреждения пакета, но это остаётся на усмотрение клиента и отключаемо в случае ненадобности.
|
||||
|
||||
Поток привязан к транспортному протоколу, по которому осуществляется физическая передача пакетов и существует в рамках жизни сессии. Один транспортный протокол может использоваться сразу несколькими потоками. Все потоки двунаправленны, т.е. пакеты могут отправляться как от клиента к серверу, так и наоборот.
|
238
Рукопожатие.md
238
Рукопожатие.md
@ -1,238 +0,0 @@
|
||||
# Рукопожатие
|
||||
|
||||
**ВНИМАНИЕ!** _Стиль данного файла не вполне соответствует статусу "неформальной" версии спецификации и слишком заостряет внимание на деталях реализации. Следует иметь ввиду, что в конечном счёте всё может и будет изменено._
|
||||
|
||||
На *рукопожатие* возложена задача создать между *клиентом* и *сервером* новый *поток* на взаимно согласованных условиях. Это включает в себя определение готовности коммуникации по *протоколу*, согласование версии *протокола* в пределах *сессии* (если создаётся новая), а также согласование различных параметров *потока*. *Рукопожатие* может быть "открытое", то есть в котором тело пакета представлено в нешифрованном виде и которое ведёт к созданию нешифрованного *потока*, и "шифрованное", в котором тело пакета зашифровано, подписано, и которое ведёт к созданию шифрованного *потока*. При выполнении этой процедуры используются уникальные форматы пакетов, несоответствующие формальному определению *события*. Формат пакетов использующийся в *рукопожатии* представлен ниже.
|
||||
|
||||
Существуют следующие типы *рукопожатия*:
|
||||
|
||||
- Первый тип: то, что создаёт новый *поток* и одновременно новую *сессию*
|
||||
- Второй тип: то, что создаёт новый *поток* в рамках существующей *сессии*
|
||||
- Третий тип: то, что восстанавливает существующий *поток* из десинхронизированного состояния в рабочее
|
||||
|
||||
*Рукопожатие* любого типа выполняется ровно в два действия:
|
||||
|
||||
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 случайных байт, полученные *сервером* с помощью криптостойкого генератора случайных чисел. <!--TODO: вероятно, что стоит перенести это и пункт ниже в соответствующие разделы про поток и сессию-->
|
||||
|
||||
### 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 |
|
||||
----------------------------------------------------------------------------------------------------
|
||||
```
|
||||
|
||||
<!--TODO: параметры SID'ов можно свести к четырём пунктам, но это повышает вероятность ошибки при формализации и реализации-->
|
||||
|
||||
|
||||
|
||||
## 3.3. Запрос
|
||||
|
||||
*Клиент* может отправлять запрос *рукопожатия* второго типа только при условии существования *сессии* между ним и *сервером*, а третьего типа - лишь при условии, что в теле запроса указан существующий *поток* в десинхронизированном состоянии.
|
||||
|
||||
В теле любого пакета-запроса *рукопожатия* должны быть указаны NoiseParameters и может быть указано ExtensionsEnum, содержащее перечень поддерживаемых *клиентом* расширений *протокола*. В теле пакета-запроса *рукопожатия* первого типа должна быть ProtocolVersion, в формате, указанном в разделе 2.2.. В теле пакета-запроса *рукопожатия* второго типа должен содержаться SessionSecret, к которой относится создаваемый *поток*. В теле пакета-запроса *рукопожатия* третьего типа должен быть указан StreamSecret, состояние которого предполагается восстанавливать.
|
||||
|
||||
В шифрованном *рукопожатии*, тело запроса шифруется с помощью заведомо-известного *клиенту* публичного ключа *сервера*. <!--TODO: тут и вообще везде применить ту новую терминологию на тему криптографии--> Задаваемые криптографические алгоритмы и ключи, передаваемые *клиентом* в шифрованном запросе *рукопожатия* - распространяются исключительно на создаваемый или восстанавливаемый *поток*.
|
||||
|
||||
В запросе шифрованного *рукопожатия* любого типа должны быть следующие параметры:
|
||||
|
||||
- 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
|
||||
|
||||
<!--TODO: вот тут тоже переделать с новой терминологией-->
|
@ -1,59 +0,0 @@
|
||||
# Сериализация и формат LBM
|
||||
|
||||
Данный формат сериализации используются в теле событий зарезервированных типов. Данные сериализованные в формат *LBM* (AKA "данные в формате *LBM*") являются расположенными последовательно ячейками с ключом, длинной значения и значением, в неопределённом порядке относительно друг-друга. Положение ячеек относительно друг-друга не детерминированно и они могут быть намеренно перемешаны. Отсутствие каких-либо ячеек вообще обозначается единичным нулевым байтом.
|
||||
|
||||
Ключ является однобайтным числом без знака. Ключ всегда больше нуля. В контексте протокола и событий зарезервированного типа, некоторые ключи имеют особое значение. Длинна значения является двухбайтным числом без знака. Если ячейка пуста, то длинна нулевая. Значением является произвольная последовательность байт.
|
||||
|
||||
Одна ячейка имеет следующий вид:
|
||||
|
||||
```text
|
||||
B: byte(s)
|
||||
|
||||
|------------|
|
||||
| Key: 1B |
|
||||
|------------|
|
||||
| Length: 2B |
|
||||
|------------|
|
||||
| Value: ~B |
|
||||
|------------|
|
||||
```
|
||||
|
||||
Пример в шестнадцатеричном представлении:
|
||||
|
||||
```text
|
||||
Length in bytes
|
||||
|
|
||||
VVVV
|
||||
0xDA00080123456789ABCDEF
|
||||
^^ ^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
Key Value
|
||||
```
|
||||
|
||||
Следовательно, форматированные данные целиком имеют вид:
|
||||
|
||||
```text
|
||||
|------------|
|
||||
| Cell 1: ~B |
|
||||
|------------|
|
||||
| Cell 2: ~B |
|
||||
|------------|
|
||||
| ... |
|
||||
|------------|
|
||||
| Cell N: ~B |
|
||||
|------------|
|
||||
```
|
||||
|
||||
Пример в шестнадцатеричном представлении:
|
||||
|
||||
```text
|
||||
Cell #4 (1B)
|
||||
Cell #2 (4B) |
|
||||
| |
|
||||
VVVVVVVVVVVVVV VVVVVVVV
|
||||
0xDA00080123456789ABCDEFF10004FEDCBA98220000340001FF... (and so on)
|
||||
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^
|
||||
| |
|
||||
Cell #1 (8 bytes length) |
|
||||
Cell #3 (empty cell)
|
||||
```
|
14
Сессия.md
14
Сессия.md
@ -1,3 +1,15 @@
|
||||
# Сессия
|
||||
|
||||
Сессия является связующим звеном для потоков, созданных одним узлом. У каждой сессии есть свой набор параметров, задаваемый в момент её создания. Этот набор включает в себя: используемую версия протокола, набор расширений и секрет сессии (SessionSecret), нужный для создания новых потоков в пределах текущей сессии. Сессия создаётся при первом рукопожатии клиента с сервером. При уничтожении сессии - уничтожаются все ассоциированные с ней потоки.
|
||||
*Сессия* объединяет в себе несколько *потоков*, созданных одним *клиентом*. Все *пакеты*, отправленные в любом из *потоков* принадлежащих одной *сессии*, интерпретируются как *пакеты* от одного и того-же *клиента*.
|
||||
|
||||
*Сессия* создаётся в следствии *рукопожатия* определённого типа. При создании *сессии* всегда создаётся один принадлежащий ей *поток*. Создание *сессии* может быть провалено в следствие передачи некорректной версии *протокола*.
|
||||
|
||||
*Сессия* существует до тех пор, пока в её рамках существует хотя бы один *поток*, при уничтожении последнего *потока* в ней - *сессия* уничтожается. *Клиент* может явно запросить у *сервера* уничтожение *сессии*. При уничтожении *сессии* также уничтожаются все принадлежащие её *потоки*. Если *сервер* собирается уничтожить *сессию* по своей воле, то ему следует предупредить об этом *клиента*.
|
||||
|
||||
У каждой *сессии* есть собственный, независимый от других *сессий*, набор параметров, который задаётся *клиентом* во время её создания и не может быть изменён в последствии. *Сервер* должен отклонять некорректные конфигурации параметров. В набор параметров *сессии* входит версия *протокола*, которая требуется для обеспечения совместимости. Оба *узла* должны коммуницировать в рамках установленной версии и имеют право уничтожить *сессию*, в случае несоблюдения этого условия.
|
||||
|
||||
<!--
|
||||
TODO:
|
||||
1. указать где надо конкретные системные события
|
||||
2. исключения?
|
||||
-->
|
||||
|
@ -1,5 +0,0 @@
|
||||
# Событие
|
||||
|
||||
Событие представляет из себя минимальную смысловую единицу, которой могут обмениваться узлы в соединении. Событиями из зарезервированного диапазона типов оперирует протокол, с целью управления соединением, все остальные события могут использоваться уровнем приложения для прочих нужд. Событие содержит тип и тело. Тип описывает способ интерпретации тела, а тело содержит произвольные данные, размер которых ограничен значением, обозначенным на этапе хэндшейка. При попытке отправки приложением данных, превышающих этот лимит, на стороне отправляющего узла должна возникнуть ошибка. Узел-получатель не должен предполагать, что получает данные валидной длинны и должен производить проверки самостоятельно.
|
||||
|
||||
Тип события является двухбайтным целым числом без знака. Событие может предполагать ответ от противоположного узла или не требовать оный. Наличие необходимости ответа определяется типом, в каждом случае индивидуально. Корректный тип события никогда не равен нулю.
|
@ -1,3 +0,0 @@
|
||||
# Транспортный адаптер
|
||||
|
||||
Адаптер представляет из себя нечто, способное вести коммуникацию в рамках конкретного транспортного протокола, преобразовывать передаваемый ему произвольный набор байт в форму, приемлимую транспортным протоколом и корректно воспринимаемую другим адаптером того-же вида, а также выполнять обратное преобразование. В рамках базового протокола определены лишь несколько адаптеров, предназначенные для использования в качестве транспорта протоколы UDP, TCP, HTTP/1.1 и HTTP/1.1+TLS1.3.
|
Loading…
Reference in New Issue
Block a user