**ВНИМАНИЕ!** _Стиль данного файла не вполне соответствует статусу "неформальной" версии спецификации и слишком заостряет внимание на деталях реализации. Следует иметь ввиду, что в конечном счёте всё может и будет изменено._
На*рукопожатие* возложена задача создать между *клиентом* и *сервером* новый *поток* на взаимно согласованных условиях. Это включает в себя определение готовности коммуникации по *протоколу*, согласование версии *протокола* в пределах *сессии* (если создаётся новая), а также согласование различных параметров *потока*. *Рукопожатие* может быть "открытое", то есть в котором тело пакета представлено в нешифрованном виде и которое ведёт к созданию нешифрованного *потока*, и "шифрованное", в котором тело пакета зашифровано, подписано, и которое ведёт к созданию шифрованного *потока*. При выполнении этой процедуры используются уникальные форматы пакетов, несоответствующие формальному определению *события*. Формат пакетов использующийся в *рукопожатии* представлен ниже.
Существуют следующие типы *рукопожатия*:
- Первый тип: то, что создаёт новый *поток* и одновременно новую *сессию*
- Второй тип: то, что создаёт новый *поток* в рамках существующей *сессии*
- Третий тип: то, что восстанавливает существующий *поток* из десинхронизированного состояния в рабочее
*Рукопожатие* любого типа выполняется ровно в два действия:
2.*Сервер* отвечает *клиенту* либо своей частью параметров, либо ошибкой
Пакет *рукопожатия* состоит из заголовка и тела. В заголовок входят магическое число протокола и флаг шифрования. Тело представляет из себя данные сериализованные в формат LBM. Соответствие ключей ячеек LBM и их содержимого приведено ниже.
Флаг шифрования является восьмибитным целым числом и может принимать одно из двух значений:
Базовые параметры, использующиеся в *рукопожатии* и их соответствие ключам ячеек LBM. Обратите внимание, что представленные здесь ключи ячеек не имеют отношения к зарезервированным, которые указаны в разделе 9.
Является однобайтовым битовым полем, описывает параметры шума в *потоке*. Структура поля выглядит примерно так:
`[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 с текстовым описанием ошибки, без завершающего нулевого байта.
## Криптографические параметры
Тоже самое, что и базовые параметры, только связанное с криптографией.
<!--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: вот тут тоже переделать с новой терминологией-->