Около-готова неформальная версия спеки

This commit is contained in:
Shr3dd3r 2024-04-24 20:23:22 +03:00
parent c27ecd812d
commit 08001d7aaa
12 changed files with 136 additions and 10 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
_old/*
schemes/
Терминология.md

View File

@ -1,12 +1,12 @@
# Неформальная спецификация протокола Stadium версии 1.0
Stadium это протокол для безопасной коммуникации общего назначения, работающий поверх обширного перечня транспорта и архитектурно не зависящий от оного. Данная спецификация описывает базу, на основе которой могут быть реализованы расширения (_SPX - Stadium Protocol eXtension_) для более конкретных нужд. <!--Помимо прочего, данный протокол служит основой для полнофункционального мессенджера Marafon, спецификация расширения которого находится в папке `SPX/Marafon/`.-->
Stadium это протокол для безопасной коммуникации общего назначения, работающий поверх обширного перечня транспорта и архитектурно не зависящий от оного. Данная спецификация описывает базу, на основе которой могут быть реализованы расширения для более конкретных нужд. <!--Помимо прочего, данный протокол служит основой для полнофункционального мессенджера Marafon, спецификация расширения которого находится в папке `SPX/Marafon/`.-->
Основной фокус при работе над этим проектом идёт на:
- Минимизация оверхэда и возможность функционирования в условиях низкой пропускной способности канала;
- Минимизацию оверхэда и возможность функционирования в условиях низкой пропускной способности канала;
- Приемлимое функционирование в условиях больших потерь пакетов;
- Поддержка широкого спектра транспортных протоколов "из коробки";
- Поддержку широкого спектра транспортных протоколов "из коробки";
- Гибкое и кастомизируемое шифрование;
- Расширяемость и возможность подгонки под конкретные задачи.

32
SPX.md Normal file
View File

@ -0,0 +1,32 @@
# 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]`

View File

@ -18,8 +18,6 @@
- **Private Identity Key**: долговременный приватный ключ. Хранится у сервера и используется для расшифровки полученных запросов рукопожатия.
- **Public Identity Key**: долговременный публичный ключ. Ассоциирован с сервером и используется для шифрования запроса рукопожатия при создании шифрованного потока.
Значительная часть функциональности протокола, в том числе касающаяся криптографии, опциональна в том смысле, что может не использоваться в рамках отдельного потока. К примеру, в нём может быть не только настроено количество шума, но и полностью отключен. Таким-же образом, коммуникация может происходить вовсе без шифрования, что может быть полезно, например, для снижения нагрузки на железо, когда протокол транспортного уровня уже обеспечивает требуемый уровень приватности.
## Пример коммуникации двух узлов

View File

@ -38,8 +38,8 @@
7. Пакет
7.1. Заголовок пакета
7.1.1. Код аутентификации [Packet Authentication Code, PAC, CltPAC, SrvPAC; проверка целостности и достоверности]
7.1.2. Флаги
7.1.3. Идентификатор ответа [RespId; мб стоит сделать PacketID и обязательным для всех, дабы перезапрашивать в случае повреждения]
7.1.2. Флаги [сжатие, наличие шума, наличие идентификатора]
7.1.3. Идентификатор [PacketID; присутствует только если включены подтверждения доставки или требуется ответ]
7.2. Полезная нагрузка
7.2.1. Шум в полезной нагрузке пакета
7.3. Структура и шифрование

26
Пакет.md Normal file
View File

@ -0,0 +1,26 @@
# Пакет
Пакет это физическая единица информации протокола стадиум. Пакет состоит из идентификатора потока, заголовка и тела. Применимость шифрования как такового, криптографические алгоритмы и алгоритмы сжатия - определяются потоком, по которому передаётся пакет. В шифрованном потоке, заголовок шифруется вместе с телом.
В заголовке пакета находятся такие данные как: код аутентификации пакета (Packet Authentication Code), который представляет из себя хэш от обработанных данных и если пакет передаётся по шифрованному потоку, то он дополнительно подписан; флаги наличия сжатия, шумовых данных и идентификатора; и идентификатор пакета, который присутствует лишь в случае, если в потоке включено подтверждение доставки и/или к данному пакету предполагается ответ.
В теле пакета содержится произвольное событие и шум (если включен на уровне потока). Ограничения на максимальный размер тела пакета протокола стадиум нет, но оно может опционально подстраиваться под максимальный размер пакета протокола транспортного уровня. В таком случае, если событие не помещается в лимит транспорта - оно разбивается на несколько стадиумных пакетов, которые представляют из себя пачку пакетов.
## Ориентировочная схема структуры пакета
```text
B: byte(s)
|-------------------------|
| StreamId: 4B |
|-------------------------|
| PAC: >=8B |
|-------------------------|
| Flags: 1B |
|-------------------------|
| PacketId (optional): 2B |
|-------------------------|
| Body: >0B |
|-------------------------|
```

View File

@ -1,7 +1,9 @@
# Поток
Поток - это логический канал для передачи пакетов, существующий в пределах конкретной сессии. Потоки могут создаваться и уничтожаться по инициативе клиента, сервер может только ограничивать создание новых потоков или уничтожать потоки исходя из соображений защиты от DoS-атак. При создании новой сессии всегда создаётся как минимум один поток.
Поток - это логический канал для передачи пакетов, существующий в пределах конкретной сессии. Потоки могут создаваться и уничтожаться по инициативе клиента, сервер может только ограничивать создание новых потоков или уничтожать потоки исходя из соображений защиты от DoS-атак. При создании сессии всегда создаётся как минимум один поток. Клиенты могут создавать потоки только в пределах собственной сессии.
У потока есть некий набор параметров, часть из которых он разделяет с сессией. У каждого потока есть свой уникальный идентификатор. Поток может быть как "шифрованным", так и "нешифрованным", что влияет на формат пакетов: в нешифрованном потоке, идентификатор потока статичен, а в шифрованном - обновляется и уникален для каждого пакета. Шифрованный поток может находиться в одном из двух состояний: "работает" или "рассинхронизирован", а нешифрованный только в первом. Поток может гарантировать последовательность отправки пакетов и информирование в случае недоставки или повреждения пакета, но это остаётся на усмотрение клиента и отключаемо в случае ненадобности.
У потока есть некий набор параметров, часть из которых он разделяет с сессией. У каждого потока есть свой уникальный идентификатор (StreamId) и секрет (StreamSecret), последний задаётся на этапе создания нового потока. Поток может быть как "шифрованным", так и "нешифрованным", что влияет на формат пакетов: в нешифрованном потоке, идентификатор потока статичен, а в шифрованном - обновляется и уникален для каждого пакета. Шифрованный поток может находиться в одном из двух состояний: "работает" или "рассинхронизирован", а нешифрованный только в первом. В случае непредвиденного нарушения работоспособности шифрованного потока, он переходит в состояние "рассинхронизирован", а вернуть его обратно в состояние "работает" клиент может путём отправки серверу пакета со специальным видом рукопожатия, содержащим StreamSecret. <!--TODO: мб написать чуть подробнее, что за "непредвиденное нарушение"-->
Значительная часть функциональности протокола, в том числе касающаяся криптографии, опциональна в том смысле, что может не использоваться в рамках отдельного потока. К примеру, в нём может быть не только настроено количество шума, но и полностью отключен. Таким-же образом, коммуникация может происходить вовсе без шифрования, что может быть полезно, например, для снижения нагрузки на железо, когда протокол транспортного уровня уже обеспечивает требуемый уровень приватности. Поток также может гарантировать последовательность отправки пакетов и информирование в случае недоставки или повреждения пакета, но это остаётся на усмотрение клиента и отключаемо в случае ненадобности.
Поток привязан к транспортному протоколу, по которому осуществляется физическая передача пакетов и существует в рамках жизни сессии. Один транспортный протокол может использоваться сразу несколькими потоками. Все потоки двунаправленны, т.е. пакеты могут отправляться как от клиента к серверу, так и наоборот.

View File

@ -1,5 +1,7 @@
# Рукопожатие
**ВНИМАНИЕ!** _Стиль данного файла не вполне соответствует статусу "неформальной" версии спецификации и слишком заостряет внимание на деталях реализации. Следует иметь ввиду, что в конечном счёте всё может и будет изменено._
На *рукопожатие* возложена задача создать между *клиентом* и *сервером* новый *поток* на взаимно согласованных условиях. Это включает в себя определение готовности коммуникации по *протоколу*, согласование версии *протокола* в пределах *сессии* (если создаётся новая), а также согласование различных параметров *потока*. *Рукопожатие* может быть "открытое", то есть в котором тело пакета представлено в нешифрованном виде и которое ведёт к созданию нешифрованного *потока*, и "шифрованное", в котором тело пакета зашифровано, подписано, и которое ведёт к созданию шифрованного *потока*. При выполнении этой процедуры используются уникальные форматы пакетов, несоответствующие формальному определению *события*. Формат пакетов использующийся в *рукопожатии* представлен ниже.
Существуют следующие типы *рукопожатия*:

View File

@ -0,0 +1,59 @@
# Сериализация и формат 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)
```

3
Сессия.md Normal file
View File

@ -0,0 +1,3 @@
# Сессия
Сессия является связующим звеном для потоков, созданных одним узлом. У каждой сессии есть свой набор параметров, задаваемый в момент её создания. Этот набор включает в себя: используемую версия протокола, набор расширений и секрет сессии (SessionSecret), нужный для создания новых потоков в пределах текущей сессии. Сессия создаётся при первом рукопожатии клиента с сервером. При уничтожении сессии - уничтожаются все ассоциированные с ней потоки.

View File

@ -1,5 +1,5 @@
# Событие
Событие представляет из себя минимальную смысловую единицу, которой могут обмениваться узлы в соединении. Событиями из зарезервированного диапазона типов (см. `6.2.`) оперирует протокол для управления соединением, все остальные события могут использоваться уровнем приложения для прочих нужд. Событие содержит тип и тело. Тип описывает способ интерпретации тела, а тело содержит произвольные данные, размер которых ограничен значением, обозначенным на этапе хэндшейка. При попытке отправки приложением данных, превышающих этот лимит, на стороне клиента стадиум должна возникнуть ошибка. Сервер не должен предполагать, что получает данные валидной длинны и должен производить проверки самостоятельно.
Событие представляет из себя минимальную смысловую единицу, которой могут обмениваться узлы в соединении. Событиями из зарезервированного диапазона типов оперирует протокол, с целью управления соединением, все остальные события могут использоваться уровнем приложения для прочих нужд. Событие содержит тип и тело. Тип описывает способ интерпретации тела, а тело содержит произвольные данные, размер которых ограничен значением, обозначенным на этапе хэндшейка. При попытке отправки приложением данных, превышающих этот лимит, на стороне отправляющего узла должна возникнуть ошибка. Узел-получатель не должен предполагать, что получает данные валидной длинны и должен производить проверки самостоятельно.
Тип события является двухбайтным целым числом без знака. Событие может предполагать ответ от противоположного узла или не требовать оный. Наличие необходимости ответа определяется типом, в каждом случае индивидуально. Корректный тип события никогда не равен нулю.

View File

@ -0,0 +1,3 @@
# Транспортный адаптер
Адаптер представляет из себя нечто, способное вести коммуникацию в рамках конкретного транспортного протокола, преобразовывать передаваемый ему произвольный набор байт в форму, приемлимую транспортным протоколом и корректно воспринимаемую другим адаптером того-же вида, а также выполнять обратное преобразование. В рамках базового протокола определены лишь несколько адаптеров, предназначенные для использования в качестве транспорта протоколы UDP, TCP, HTTP/1.1 и HTTP/1.1+TLS1.3.