Тотальный передел, глава вторая

This commit is contained in:
Shr3dd3r 2024-03-11 21:30:22 +03:00
parent 07a2413612
commit ed54ecb7f9
18 changed files with 297 additions and 550 deletions

12
.gitignore vendored
View File

@ -1,10 +1,2 @@
Data types/DATA TYPES.md
SPX/
_something.md
LBM.md
Reserved LBM keys.md
Reserved events/idk/
Reserved events/BaseResponse/
Reserved events/BaseSession/
Reserved events/BaseUnsigned/
Sessions.md
_old/*
1.2. Терминология.md

View File

@ -1,24 +0,0 @@
# Общие принципы поведения узлов
## Обязанности узла
1. Оповещать об ошибке, возникшей во время обработки _события_ другого _узла_, но только если ошибка связана с его некорректной формой.
2. По умолчанию отклонять все _события_ с ложной подписью в _подписанном соединении_.
## Обязанности сервера
1. Оповещать _узел_ (будь то _клиент_ или _сервер_ в федерации) о всех ошибках, возникших во время обработки его _события_, кроме связанных с безопасностью. <!--TODO: сформулировать точнее-->
2. Отдавать предпочтение данным других _серверов_ в федерации, нежели _клиентов_.
3. Отдавать предпочтение сетевым настройкам входящих соединений, нежели локальным (не считая лимиты).
## Обязанности клиента
1. Не сообщать _серверу_ ни о каких ошибках на своей стороне.
2. По умолчанию блокировать до решения юзера обработку любых _событий_, содержащих ложную подпись. <!--TODO: это скорее про фронтенд-->
3. Явно уведомлять юзера при возникновении проблем с безопасностью, как минимум по умолчанию.

View File

@ -1,3 +0,0 @@
# Структуры данных
Сия спецификация, помимо всего прочего, также упоминает и определяет некоторые необходимые типы и структуры данных. В директории `Data types/` вы найдёте их описания и примеры декларации на языке C++.

View File

@ -1,20 +0,0 @@
# Crypto::Algorithm
Описывает криптографический алгоритм вместе с его базовой конфигурацией.
## Структура
`[AlgorithmID: 1B][AlgorithmParams: 1B]`
## C++
```C++
namespace Crypto {
struct Algorithm {
AlgorithmID ID;
AlgorithmParams Params;
};
}
```

View File

@ -1,71 +0,0 @@
# Crypto::AlgorithmID
Является перечислением однобайтных целых чисел без знака, соответствующих идентификаторам допустимых к использованию в базовом протоколе криптографических (и не только) алгоритмов: хэшей, контрольных сумм, симметричного и ассиметричного шифрования.
<!--TODO: мб всё-таки не-криптуху стоит вынести?-->
## C++
```C++
namespace Crypto {
enum struct AlgorithmID : uint8_t {
None = 0,
// Checksums
CRC = 11, // 8/16/32/64/etc.
fletcher, // 8/16/32/etc.
Adler, // 32/etc.
// Non-crypto hashes
Murmur64A = 31,
Murmur3, // 32/128
Spooky, // 128/etc.
// Cryptographic hashes
BLAKE2b = 51,
BLAKE3,
GOST,
HAS160,
HAVAL,
MD2,
MD5,
RIPEMD,
SHA1,
SHA2,
SHA3,
SHAKE, // 128/256
Skein,
Snefru,
Streebog,
Tiger,
Whirlpool,
// Symmetric key block ciphers
Blowfish = 81,
Twofish,
Threefish,
TripleDES,
HPC,
MARS,
SAFER2PLUS,
SQUARE,
CRYPTON,
AES, // AKA Rijndael, GCM/CBC/CTR/etc.
MAGENTA,
XTEA1,
XTEA3,
KHAZAD,
Camellia,
Salsa20,
CAST5,
CAST6,
Kuznyechik,
MESH,
Akelarre,
RC6
};
}
```
<!--
TODO: симметрики распилить по категориям на блоковые и поточные
TODO: ассиметрики и подписи (RSA, YAK, CramerShoup system, Paillier cryptosystem, curves (25519 & 448), ElGamal, etc.)
-->

View File

@ -1,13 +0,0 @@
# Crypto::AlgorithmParams
Описывает параметры алгоритма, например размер ключа. Является двухбайтным целым без знака.
## C++
```C++
namespace Crypto {
typedef uint16_t AlgorithmParams;
}
```

View File

@ -1,9 +0,0 @@
# EventAsyncID
Является уникальным двухбайтным целым числом без знака и предназначен для определения отношения запросов к ответам при асинхронном обмене _событиями_.
## C++
```C++
typedef uint16_t EventAsyncID;
```

View File

@ -1,9 +0,0 @@
# LocalObjectID
Уникальный идентификатор объекта на сервере, является восьмибайтным числом без знака.
## C++
```C++
typedef uint64_t LocalObjectID;
```

View File

@ -1,45 +0,0 @@
# События
## Категории и типы
Существует три уровня категорий _событий_:
**Надкатегория** описывает виды взаимодействующих _узлов_. Не указывается и зависит от контекста. Варианты: _Client2Server_ (_событие_ сгенерированное _клиентом_ для _сервера_), _Server2Client_ (_событие_ сгенерированное _сервером_ для _клиента_), _Peer2Peer_ (_событие_ сгенерированное _узлом_ для другого _узла_ того-же ранга, в том числе это касается _серверов_ в федерации).
**Категория** описывает _события_ одного класса. Является однобайтным числом без знака. Всегда больше нуля. Например: _Authentication_, _Object_, т.п.
**Подкатегория** описывает конкретное _событие_ из класса, соответствующего категории. Является однобайтным числом без знака. Всегда больше нуля. Например: _Login_, _GetContents_, т.п.
Все категории вместе - являются типом _события_. Информацию про зарезервированные типы вы можете найти в [Reserved events.md](Reserved%20events.md).
## Содержание и структура
Все _события_ содержат идентификатор _потока_ размером 4 байта. Подробнее - в [Streams.md](Streams.md).
_События_ в _шифрованном соединении_ содержат хэш, применённый к зашифрованному блобу (см. ниже). Этот хэш имеет размер равно или более 16 байт и подписан с помощью закрытого ключа подписи отправляющего. Он гарантирует достоверность содержания _события_ на уровне прямого подключения между двумя _узлами_. Используемые алгоритмы подписи и хэширования определяются на этапе рукопожатия.
Все _события_ содержат асинхронный идентификатор _события_, который является двухбайтным целым числом без знака и предназначен для определения отношения запросов к ответам при асинхронном обмене _событиями_ в конкретном _потоке_.
Все _события_ содержат тип (см. выше раздел "Категории и типы").
_События_ могут содержать полезную нагрузку, либо один нулевой байт, свидетельствующий о её отсутствии. Данная версия протокола не накладывает ограничений на формат полезной нагрузки, за исключением базовых событий, которые представлены данными в формате _LBM_. Описание всех предопределённых в базовом протоколе ключей ячеек _LBM_ доступно в [Reserved LBM keys.md](Reserved%20LBM%20keys.md). При использовании формата _LBM_, в полезную нагрузку могут добавляться шумовые данные. Неизвестные ключи при обработке полезной нагрузки игнорируются.
_События_ в _шифрованном соединении_ содержат блоб, зашифрованный с помощью актуального ключа _потока_ и утверждённого алгоритма симметричного шифрования. Этот блоб содержит асинхронный идентификатор, тип события и полезную нагрузку. В нешифрованном _соединении_, указанные поля находятся в "сыром" виде.
Исходя из всего вышеописанного, минимальный размер сериализованного в бинарный вид _события_ в нешифрованном _соединении_ составляет 9 байт, а его итоговая структура выглядит следующим образом:
`[Stream ID: 4B][Async ID: 2B][Event Type: 2B][Payload: >0B]`
В свою очередь, _события_ в _шифрованном соединении_ имеют размер не менее 25-и байт и следующую структуру:
```text
[Stream ID: 4B][Signed Hash: >=16B][Encrypted Blob: >=5B]
/ | \
[Async ID: 2B][Event Type: 2B][Payload: >0B]
```
Максимальный размер _события_ не нормирован. Ответственность за менеджмент разделения _события_, в случае, если оно не помещается в один пакет, остаётся на транспортном уровне. Максимальный размер полезной нагрузки _события_ определяется на этапе рукопожатия.

View File

@ -1,147 +0,0 @@
# Рукопожатие
На рукопожатие возложена задача выяснить способность _узлов_ устанавливать соединение по описываемому протоколу и согласовать подключение с избранными параметрами каждой стороны. На выбор существует два типа хэндшейка: открытый и зашифрованный. Второй дополнительно требует знать публичный ключ целевого _узла_.
## Запрос рукопожатия
После успешного подключения к целевому _узлу_ через избранный транспорт происходит обмен параметрами обоих сторон. Инициирующий соединение _узел_ отправляет пакет с магический числом, флагом шифрования и сериализованными данными в формат _LBM_. В случае с зашифрованным хэндшейком, сериализованные данные дополнительно зашифровываются с помощью публичного ключа целевого _узла_.
Структура пакета с запросом рукопожатия выглядит следующим образом:
`[magic number: 8B][encryption flag: 1B][LBM-formatted data: >0B]`
### Магическое число
Магическим числом является следующая последовательность байт:
```
HEX: 0x53 0x74 0x61 0x64 0x69 0x75 0x6d 0x50
DEC: 83 116 97 100 105 117 109 80
ASCII: S t a d i u m P
```
Если _узел_ получает пакет, который не содержит магического числа на указанной позиции, то он должен молча закрыть соединение.
### Флаг шифрования
Запрос шифрованного хэндшейка предполагает установку этого флага в значение `0x7a`. Все остальные значения должны интерпретироваться как запрос открытого хэндшейка.
### Данные
Данные в формате _LBM_ содержат следующие ячейки:
- Версия протокола
- _Ключ:_ `0x01`
- _Тип:_ `uint8_t`
- _Обязательна:_ да
- Поддерживаемая запрашивающим _узлом_ версия протокола.
- `0b10000000`: зарезервировано
- `0b01111111`: номер версии
- Алгоритмы хэширования полезной нагрузки
- _Ключ:_ `0x02`
- _Тип:_ `Crypto::Algorithm[]`
- _Обязательна:_ только для шифрованного ХШ
- Описывает запрашиваемые к использованию в этом соединении хэш-функции для формирования хэша полезной нагрузки. <!--TODO: упомянуть в структуре события использование нескольких алгоритмов совместно-->
- Алгоритм подписи
- _Ключ:_ `0x03`
- _Тип:_ `Crypto::Algorithm`
- _Обязательна:_ только для шифрованного ХШ
- Описывает используемый запрашивающим _узлом_ алгоритм подписи.
- Публичный ключ подписи
- _Ключ:_ `0x04`
- _Тип:_ `char[]`
- _Обязательна:_ только для шифрованного ХШ
- Публичный ключ подписи запрашивающего _узла_, который будет использован для проверки достоверности событий в соединении.
- Алгоритмы симметричного шифрования
- _Ключ:_ `0x05`
- _Тип:_ `Crypto::Algorithm[]`
- _Обязательна:_ только для шифрованного ХШ
- Описывает запрашиваемые к использованию в этом соединении симметричные алгоритмы шифрования.
- Приватный ключ
- _Ключ:_ `0x06`
- _Тип:_ `char[]`
- _Обязательна:_ только для шифрованного ХШ
- Используемый в этом соединении приватный ключ, размера соответствующего избранному алгоритму симметричного шифрования.
- Параметры шума
- _Ключ:_ `0x07`
- _Тип:_ `uint8_t`
- _Обязательна:_ нет
- Описывает параметры шума в соединении:
- `0b11100000`: выделено под настройку количества _шумовых пакетов_
- `0b000`: отсутствие _шумовых пакетов_
- `0b001`: соотношение умовых пакетов_ к реальным - 1:8
- `0b010`: соотношение - 1:4
- `0b011`: 1:2
- `0b100`: 1:1
- `0b101`: 2:1
- `0b110`: 4:1
- `0b111`: зарезервировано
- `0b00011000`: выделено под настройку размера _шумовых пакетов_
- `0b00`: случайный размер, в пределах от минимального _пакета_ с нулевой полезной нагрузкой, до размера _пакета_ с полезной нагрузкой размерности, которую должен передать целевой _узел_ при одобрении рукопожатия
- `0b01`: случайный размер, на основе среднего размера передаваемого пакета +- 25% (но всегда меньше максимального)
- `0b10`: случайный размер, в пределах от минимального, до максимального / 2
- `0b11`: зарезервировано
- `0b00000110`: выделено под настройку размера умовых данных_ в обычных _пакетах_
- `0b00`: без шумовых данных
- `0b01`: заполнение шумовыми данными до лимита полезной нагрузки
- `0b10`: заполнение шумовыми данными в случайном количестве, от нуля, до лимита полезной нагрузки
- `0b11`: зарезервировано
- `0b00000001`: зарезервировано
<!--
- Запрос транспорта (опционально)
- _Ключ:_ `0x05`
- _Тип:_ `uint8_t`
- Если существует, то интерпретируется как запрос переподключения к другому виду транспорта. TODO: Надо сделать енум.
-->
## Ответ на рукопожатие
На запрос рукопожатия с корректным магическим числом целевой _узел_ должен ответить пакетом либо с согласием, либо с ошибкой. Если это ответ с согласием, то данные в пакете должны быть зашифрованы с помощью переданного в запросе хэндшейка симметричного шифра и переданного приватного ключа. Если это ответ с ошибкой, то он может быть незашифрован, в случае, если ошибка связанна с инициализацией шифрования или версией протокола. Таким образом, запрашивающий _узел_ должен быть готов обработать оба варианта развития событий, исходя из значения флага шифрования. Если цель не поддерживает указанные при рукопожатии параметры, запрашивающий может попробовать установить соединение снова, с иными параметрами.
Пакет ответа имеет следующую структуру:
`[magic number: 8B][encryption flag: 1B][LBM-formatted data: >0B]`
### Данные
Данные в формате _LBM_ содержат следующие ячейки:
- Максимальный размер полезной нагрузки
- _Ключ:_ `0x71`
- _Тип:_ `uint16_t`
- _Обязательна:_ только для соглашения
- Всегда больше нуля. Оба _узла_ должны соблюдать размерность полезной нагрузки в событии, она всегда должна быть равна или меньше этого значения.
- Код ошибки
- _Ключ:_ `0x72`
- _Тип:_ `uint8_t`
- _Обязательна:_ только для ошибки
- Код, описывающий тип ошибки:
- `0x01`: магическое число ложно
- `0x02`: неподдерживаемая версия протокола
- `0x03`: недопустимый алгоритм хэширования
- `0x04`: недопустимый алгоритм подписи
- `0x05`: недопустимый алгоритм симметричного шифрования
- `0x06`: некорректный приватный ключ
- `0x07`: некорректный публичный ключ подписи
- `0x08`: недопустимые параметры шума
- `0x09`: невозможно корректно расшифровать данные (ложный публичный ключ целевого _узла_)
- Описание ошибки
- _Ключ:_ `0x73`
- _Тип:_ `char[]`
- _Обязательна:_ нет
- Строка в кодировке ASCII, описывающая ошибку в человекочитаемом виде.
<!--TODO: стартовый SID-->

View File

@ -1,92 +0,0 @@
# Обзор
## Терминология
Здесь перечислены используемые в данной спецификации термины, значение которых может быть не очевидно и/или не соответствует тому, которое подразумевается обычно.
### Узел
Любая машина, способная выполнять коммуникацию по протоколу Stadium. Обычно используется в контексте коммуникации двух машин одного ранга или когда ранг не имеет значения.
### Сервер
Выполняющий роль сервера _узел_ в многоранговой сети.
### Клиент
Выполняющий роль клиента _узел_ в многоранговой сети.
### Соединение/поток
Канал обмена _событиями_ между двумя _узлами_, имеющий свой идентификатор (Stream ID/SID, см. [Streams.md](Streams.md)) и набор настроек.
### Шифрованное соединение/поток
_Соединение_ между двумя _узлами_, все _события_ в котором шифруются с помощью оговорённого симметричного алгоритма и ключа, а также подписываются с помощью приватного ключа отправителя.
### Сессия
То, что устанавливается при создании первого _соединения_ между двумя _узлами_, с чем ассоциированы конкретные _потоки_ и прочие параметры.
### Событие
Структура данных, обладающая конкретными семантическими свойствами в зависимости от типа и содержащая дополнительные поля, требуемые для верного отображения и интерпретации самой структуры. Проще говоря - то, чем обмениваются и что обрабатывают _узлы_ во время коммуникации между собой.
### Шумовое событие
_Событие_ предопределённой категории, имеющее случайно-сгенерированные поля.
### LBM
Аббревиатура наименования способа форматирования (сериализации) данных, используемого для полезной нагрузки _события_, которая расшифровывается как "Linear Binary Map". Подробнее - в [LBM.md](LBM.md).
### Шумовые данные
Случайно-сгенерированные данные, помещённые в специализированную ячейку полезной нагрузки _события_.
## Пример коммуникации двух узлов
Допустим, что у нас есть два неизвестных\* друг-другу _узла_ - `p1` и `p2`. Тогда полная коммуникация по шагам (от подключения неизвестного для `p2` _узла_ - до "правильного" уничтожения сессии и закрытия соединения) будет выглядеть так:
1. `p1` запрашивает открытое (нешифрованное) рукопожатие с избранными параметрами соединения у `p2`
2. `p2` отвечает `p1` согласием со своей частью параметров
3. `p1` запрашивает публичный ключ подписи у `p2`
4. `p2` передаёт свой публичный ключ, вместе с используемым алгоритмом
5. `p1` сохраняет полученный публичный ключ и разрывает\*\* соединение и заново устанавливает его, путём отправки `p2` запроса шифрованного хэндшейка
6. `p2` отвечает согласием со своей частью параметров подключения
7. `p1` отправляет зашифрованное _событие_ с запросом какого-то объекта
8. `p2` отвечает зашифрованным _событием_, содержащее статус-код или этот объект
9. `p1` отправляет _событие_ с сообщением о намерении уничтожить сессию и ждёт в течении n времени, по истечению которого может разорвать соединение "насильно"
10. `p2` принимает запрос и прекращает любой параллельный обмен данными
11. `p2` отвечает _событием_ с кодом успешности операции
12. `p2` закрывает соединение
И в виде схемы:
```
Requesting p2's public key:
[p1] ------[raw handshake request]----> [p2]
[p1] <-----[raw handshake accept]------ [p2]
[p1] ----[request public sign key]----> [p2]
[p1] <-[public key and crypto params]-- [p2]
[p1] ---------[close session]---------> [p2]
[p1] <--------[status code OK]--------- [p2]
Reconnecting in encrypted way and requesting object:
[p1] ---[encrypted handshake request]-> [p2]
[p1] <--[encrypted handshake accept]--- [p2]
[p1] ------[request some object]------> [p2]
[p1] <-[some object data/status code]-- [p2]
[p1] ---------[close session]---------> [p2]
[p1] <--------[status code OK]--------- [p2]
```
\* - _стоит уточнить, что в реальных сценариях использования публичный ключ целевого узла скорее всего уже известен, либо коммуникация должна производиться через транспорт, гарантирующий достоверность передаваемых данных. `p1` может узнать о существовании таковых, путём отправки события специального типа. В данном примере сей шаг опущен._
<!--TODO: таки описать этот шаг ^^^-->
\*\* - _"разрыв" соединения предполагает обмен между узлами событиями с информацией о завершении сессии; в данном пункте эти шаги опущены, но упомянуты ниже._

View File

@ -1,18 +1,12 @@
# Спецификация протокола Stadium версии 1.0
# Неформальная спецификация протокола Stadium версии 0.5
Stadium это протокол для безопасной коммуникации общего назначения, работающий поверх обширного перечня транспорта и архитектурно не зависящий от оного. Данная спецификация описывает базу, на основе которой могут быть реализованы расширения (_SPX - Stadium Protocol eXtension_) для более конкретных нужд. Помимо прочего, данный протокол служит основой для полнофункционального мессенджера Marafon<!--, спецификация расширения которого находится в папке `SPX/Marafon/`-->.
Stadium это протокол для безопасной коммуникации общего назначения, работающий поверх обширного перечня транспорта и архитектурно не зависящий от оного. Данная спецификация описывает базу, на основе которой могут быть реализованы расширения (_SPX - Stadium Protocol eXtension_) для более конкретных нужд. <!--Помимо прочего, данный протокол служит основой для полнофункционального мессенджера Marafon, спецификация расширения которого находится в папке `SPX/Marafon/`.-->
Основной фокус при работе над сим проектом идёт на:
Основной фокус при работе над этим проектом идёт на:
- Возможность функционирования в условиях низкой пропускной способности канала;
- Устойчивость к цензуре;
- Поддержка широкого спектра транспортных протоколов;
- Построение децентрализованных меш-сетей (как одноуровневых, так и двухуровневых);
- Гибкое сквозное шифрование;
- Расширяемость и возможность подстраивания под конкретные задачи.
- Низкий оверхэд и возможность функционирования в условиях низкой пропускной способности канала;
- Поддержка широкого спектра транспортных протоколов из коробки;
- Гибкое и кастомизируемое сквозное шифрование;
- Расширяемость и возможность подгонки под конкретные задачи.
**ПРОЕКТ В АКТИВНОЙ РАЗРАБОТКЕ/PROJECT UNDER ACTIVE DEVELOPMENT**
All text will be translated to english later.
**Внимание!** В данный момент проект переживает глобальные архитектурные изменения. Данная версия спецификации акцентирует внимание на закреплении общих, преимущественно фундаментальных идей и концепций, потому и является *неформальной*.

View File

@ -1,21 +0,0 @@
# Зарезервированные типы событий
Некоторые категории _событий_ зарезервированы под нужды базового протокола или просто для _событий_ определённого класса. Второе носит рекомендательный характер; вы также можете использовать иные диапазоны для тех-же целей. Ниже приведены диапазоны зарезервированных значений в виде шестнадцатеричных чисел. Конкретный перечень всех зарезервированных событий и описание их структуры - есть в папке `Reserved events/`.
Зарезервировано для нужд протокола и запрещено к использованию в сторонних расширениях:
1. Категория и субкатегория `0x00`
- Запрещены к использованию.
2. Категория `0x01`
- Все субкатегории: выделены для базовых _событий_, связанных с менджментом сессии.
3. Категория `0x11`
- Все субкатегории: выделены для базовых кодов ответа, ошибок и предупреждений.
Рекомендуется к использованию расширениями в конкретных ситуациях:
1. Категории `0x12`-`0x1F` (включительно)
- Все субкатегории: для кодов ответа, ошибок и предупреждений сторонних реализаций.

View File

@ -1,43 +0,0 @@
# Категория.Субкатегория
**HEX:** `BA98`
**Возможные ответы:** [ДругаяКатегория.ДругаяСубкатегория](Example.md), [ЕщёКатегория.ЕщёСубкатегория](Example.md)
## Client2Server
Какое-то описание типа _события_. Разделы (Client2Server, Server2Client, т.д.) могут находится в произвольном порядке относительно друг-друга. Если ячеек нет, то это указывается явно.
### Ячейки
#### ObjectID
Какое-то описание, к чему применимо значение в этой ячейке, что оно из себя представляет и тому подобное.
#### PrevEventID
Тоже какое-то описание.
## Server2Client
Какие-то пояснении об отличии этого _события_ от иных версий. Если отличий нет, то раздел опускается. Если надкатегория _события_ в этом контексте не допустима, то об этом явно указывается. (см. раздел Peer2Peer для примера)
### Дополнительные ячейки
#### CryptoAsymCipher
Ещё какое-то описание.
### Отсутствующие ячейки
- **ObjectID**: описание причины, почему не требуется
## Peer2Peer
_Не применимо._

View File

@ -1,3 +0,0 @@
# Marafon SPX
_Расширение протокола Stadium для мессенджера Marafon._

View File

@ -1,26 +0,0 @@
# Потоки
**Поток** представляет из себя канал обмена _событиями_ в определённой конфигурации. _Узлы_ должны обрабатывать _события_ единообразно, вне зависимости от того, через какой _поток_ они передаются.
## Stream ID
Каждый _поток_ имеет пару собственных уникальных идентификаторов: "Stream ID", "SID". Они используются для определения принадлежности _события_ к существующим _потокам_. Для увеличения энтропии, с целью исключения анализа траффика, SID представляет из себя нефиксированное значение, уникальное для каждого _события_. Каждый из двух _узлов_ в _соединении_ имеет собственный SID.
Допустим, что есть два _узла_: `pc`, который подключается, и `ps`, к которому подключается `pc`. Тогда пусть SID, используемый _узлом_ `pc` будет называться `sidc`, а `ps` - `sids`. Для того, чтобы установить новое _соединение_, _узлы_ должны проделать следующее:
1. `pc` генерирует стартовое значение `sidc` и отправляет его _узлу_ `ps` вместе с алгоритмами симметричного шифрования и хэширования для обработки `sids`, а также симметричный ключ соответствующего алгоритму размера.
2. `ps` принимает данные и отвечает _узлу_ `pc` стартовым значением `sids`, алгоритмами шифрования и хэширования `sidc`, а также симметричный ключ соответствующего алгоритму размера.
При этом, `ps` должен иметь единую конфигурацию алгоритмов симметричного шифрования, хэш-функции и используемый ключ для обработки SID всех _потоков_. Перед использованием в отправляемом _событии_, _узел_ должен обработать прежнее значение SID, а именно: зашифровать его с использованием избранного при установке _соединения_ алгоритма симметричного шифрования и ключа, а затем вычислить хэш. Если размер выходных данных хэш-функции больше размера SID, то эти данные поблочно XOR'ятся сами с собой. В реализации это может выглядеть вот так: <!--NOTICE: получше формализовать надо, скорее всего-->
```C
// size_t arr_sz: размер вывода хэш-функции
// char *arr: вывод хэш-функции
// uint32_t sid: прежний SID
sid = *(uint32_t*)arr;
for (size_t i = 1; i < arr_sz / 4; ++i)
sid = sid ^ ((uint32_t*)arr)[i];
```
Если любой из _узлов_ обнаруживает ложное значение SID в полученном _событии_, то он обязан разорвать _соединение_. <!--и сохранять статус _потока_ в течении времени, определяемого имплементацией и конфигурацией. _Узел_, инициировавший создание _потока_, может попробовать пересоздать его в течении этого времени, -->
<!--TODO: StreamRessurectionKey, восстановление потока в случае обрыва связи-->

51
Оглавление.txt Normal file
View File

@ -0,0 +1,51 @@
ВНИМАНИЕ! В этой версии оглавления обнаружены существенные недостатки, потому оно точно изменится в дальнейшем.
1. Введение
1.1. Цели и мотивация
1.2. Терминология
1.3. Пример работы в виде схемы
2. Параметры
2.1. Магическое число протокола
2.2. Версия протокола
2.3. CryptoDesc [дескриптор криптоалгоритма]
2.3.1. HashCryptoDesc
2.3.2. SymCiphCryptoDesc
2.3.3. AsymCiphCryptoDesc
2.4. CryptoPack [набор криптоалгоритмов]
2.4.1. HashCryptoPack
2.4.2. SymCiphCryptoPack
2.4.3. SignCryptoPack [набор для подписи, AsymCiphCryptoDesc + HashCryptoDesc]
2.4.4. KDCryptoPack [набор для Key Derivation, либо существующая KDF, либо SymCiphCryptoDesc + HashCryptoDesc]
2.4.4.1. KDInitValue [значение, которым впервые инициализируется KDF]
2.4.4.2. KDPrivConst [приватное значение, используемое в качестве второго аргумента KDF]
2.6. Linear Binary Map
3. Рукопожатие
3.1. Базовые параметры
3.2. Параметры криптографии
3.3. Запрос
3.4. Ответ
4. Сессия
5. Поток
5.1. Состояние
5.2. SID, SSID, CSID
5.3. Шифрование
5.4. Контроль последовательности событий
5.5. Шум
6. Событие
6.1. Заголовок
6.1.1. MAC, CMAC, SMAC [проверка целостности + проверка достоверности]
6.1.2. Асинхронный идентификатор [AID]
6.1.3. Тип события
6.2. Полезная нагрузка
6.3. Структура
6.3.1. Открытого события
6.3.2. Шифрованного события
6.4. Запрос и ответ
7. Безопасность и модель угроз
8. Распределение типов событий
- ... конкретные типы ...
9. Зарезервированные ячейки LBM
- ... конкретные ячейки ...
10. Расширения протокола
10.1. Идентификатор расширения [SPXI]
10.2. Дескриптор расширения [SPXD]

236
Рукопожатие.md Normal file
View File

@ -0,0 +1,236 @@
# Рукопожатие
На *рукопожатие* возложена задача создать между *клиентом* и *сервером* новый *поток* на взаимно согласованных условиях. Это включает в себя определение готовности коммуникации по *протоколу*, согласование версии *протокола* в пределах *сессии* (если создаётся новая), а также согласование различных параметров *потока*. *Рукопожатие* может быть "открытое", то есть в котором тело пакета представлено в нешифрованном виде и которое ведёт к созданию нешифрованного *потока*, и "шифрованное", в котором тело пакета зашифровано, подписано, и которое ведёт к созданию шифрованного *потока*. При выполнении этой процедуры используются уникальные форматы пакетов, несоответствующие формальному определению *события*. Формат пакетов использующийся в *рукопожатии* представлен ниже.
Существуют следующие типы *рукопожатия*:
- Первый тип: то, что создаёт новый *поток* и одновременно новую *сессию*
- Второй тип: то, что создаёт новый *поток* в рамках существующей *сессии*
- Третий тип: то, что восстанавливает существующий *поток* из десинхронизированного состояния в рабочее
*Рукопожатие* любого типа выполняется ровно в два действия:
1. *Клиент* запрашивает *рукопожатие* с избранными параметрами
2. *Сервер* отвечает *клиенту* либо своей частью параметров, либо ошибкой
Пакет *рукопожатия* состоит из заголовка и тела. В заголовок входят магическое число протокола и флаг шифрования. Тело представляет из себя данные сериализованные в формат LBM. Соответствие ключей ячеек LBM и их содержимого приведено ниже.
Флаг шифрования является восьмибитным целым числом и может принимать одно из двух значений:
1. `0x00` - шифрования нет.
2. `0x80` - шифрование включено.
Формат пакета *рукопожатия* следующий:
```text
[Magic number: 8 bytes][Encryption flag: 1 byte][Handshake body: non-fixed bytes]
```
## Базовые параметры
Базовые параметры, использующиеся в *рукопожатии* и их соответствие ключам ячеек LBM. Обратите внимание, что представленные здесь ключи ячеек не имеют отношения к зарезервированным, которые указаны в разделе 9.
```text
----------------------------------------------------------------------------------------
| Name | Key | Description |
|------------------|------|------------------------------------------------------------|
| NoiseParameters | 0x01 | Параметры шума в потоке |
| ProtocolVersion | 0x02 | Версия протокола, использующаяся в сессии |
| StreamSecret | 0x03 | Секрет потока |
| SessionSecret | 0x04 | Секрет сессии |
| MaxPayloadSize | 0x05 | Максимальный размер полезной нагрузки |
| ExtensionsEnum | 0x06 | Перечисление расширений |
| ErrorCode | 0x20 | Код ошибки сервера |
| ErrorDescription | 0x21 | Детальное описание ошибки |
----------------------------------------------------------------------------------------
```
### NoiseParameters
Является однобайтовым битовым полем, описывает параметры шума в *потоке*. Структура поля выглядит примерно так:
`[3 bits][2 bits][2 bits][1 bit (reserved)]`
Первые три бита выделены под настройку количества шумовых *событий*, допустимые значения:
- `0b000`: отсутствие шумовых *событий*
- `0b001`: соотношение шумовых *событий* к реальным 1:8
- `0b010`: соотношение 1:4
- `0b011`: 1:2
- `0b100`: 1:1
- `0b101`: 2:1
- `0b110`: 4:1
- `0b111`: зарезервировано
Следующие два бита выделены под настройку размера шумовых *событий*:
- `0b00`: случайный размер, в пределах от размера минимального *события* с нулевой *полезной нагрузкой*, до MaxPayloadSize
- `0b01`: случайный размер, на основе среднего размера передаваемого *события* +- 25% (но всегда меньше максимального)
- `0b10`: случайный размер, в пределах от минимального, до MaxPayloadSize / 2
- `0b11`: зарезервировано
Ещё два бита выделены под настройку размера шумовых данных в обычных *событиях*:
- `0b00`: без шумовых данных
- `0b01`: заполнение шумовыми данными до лимита *полезной нагрузки*
- `0b10`: заполнение шумовыми данными в случайном количестве, от нуля, до лимита *полезной нагрузки*
- `0b11`: зарезервировано
Последний бит зарезервирован.
### StreamSecret
Секрет *потока* представляет из себя 16 случайных байт, полученные *сервером* с помощью криптостойкого генератора случайных чисел. <!--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: вот тут тоже переделать с новой терминологией-->