# Криптография Перед данным протоколом стоит задача обеспечить гибкое шифрование с минимальным ущербом для совместимости между реализациями. Достигается это путём широкого спектра поддерживаемых "из коробки" криптографических алгоритмов и их комбинаций, вкупе с возможностью расширения этого списка в частных случаях, а также возможностью шифрования "в несколько слоёв". Реализуемая стадиумом криптографическая система, помимо базового свойства (сокрытие передаваемых данных от третьих лиц), обеспечивает следующие несколько вещей: - **Достоверность данных**: передаваемые по протоколу данные подписываются с помощью ассиметричного шифрования, алгоритмом, выбранным на этапе подключения, что исключает возможность их подмены. - **Исключение атак повторного воспроизведения**: каждый пакет снабжён уникальными идентификатором, что приводит к невозможности проведения 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 Алисы.