Files
SHiNE-server/docs/Протоколы/Преобразование_ED25519_в_X25519.md
T

152 lines
10 KiB
Markdown

# Преобразование Ed25519 в X25519
## Назначение
Этот документ фиксирует единое правило проекта SHiNE:
- публичная идентичность пользователя задаётся одним `clientKey` в формате `Ed25519`;
- подпись пользовательских сообщений и команд выполняется ключом `Ed25519`;
- шифрование личных сообщений использует ключ `X25519`, получаемый детерминированно из того же ключевого материала;
- для преобразования используется только стандартное преобразование `Ed25519 <-> X25519`;
- самодельные формулы, альтернативные хэши и нестандартные схемы преобразования в проекте не допускаются.
Цель документа: исключить путаницу между ключом подписи и ключом шифрования и зафиксировать один общий способ преобразования для клиента, сервера и будущих реализаций.
## Краткое правило
Если у пользователя есть:
- приватный ключ `Ed25519 private`;
- публичный ключ `Ed25519 public` (`clientKey`);
то для шифрования личных сообщений из них получаются:
- `X25519 private` для расшифровки своих копий;
- `X25519 public` для шифрования сообщений на этого пользователя.
При этом:
- в профиле пользователя по-прежнему публикуется только `clientKey`;
- отдельный публичный `dmEncKey` не требуется;
- владелец устройства локально может использовать один и тот же исходный секрет и для подписи, и для вывода ключа шифрования.
## Что считается стандартным преобразованием
Под "стандартным преобразованием" в проекте понимается совместимое с общепринятой практикой libsodium/NaCl преобразование:
- из `Ed25519 private` получается `X25519 private` через стандартную процедуру на основе SHA-512 и clamping;
- из `Ed25519 public` получается `X25519 public` через стандартное преобразование точки Edwards в точку Montgomery.
Иными словами:
- приватный ключ шифрования должен получаться из приватного ключа подписи стандартным способом;
- публичный ключ шифрования должен получаться из публичного `clientKey` стандартным способом;
- обе стороны должны получать одинаковый `X25519 public` по одному и тому же `clientKey`.
## Преобразование приватного ключа
Исходные данные:
- `Ed25519 private` в SHiNE рассматривается как 32-байтный seed приватного ключа.
Стандартная процедура:
1. Вычислить `SHA-512(seed32)`.
2. Взять первые 32 байта результата.
3. Применить стандартный clamping для `X25519 private`.
4. Полученный результат использовать как `X25519 private`.
Замечания:
- нельзя использовать для `X25519 private` просто исходный 32-байтный `Ed25519 seed` без преобразования;
- нельзя заменять `SHA-512` на `SHA-256`, `HMAC`, `HKDF` или произвольную пользовательскую формулу;
- clamping обязателен.
## Преобразование публичного ключа
Исходные данные:
- `clientKey` пользователя в формате `Ed25519 public` (32 байта).
Стандартная процедура:
1. Декодировать публичную точку Edwards из `Ed25519 public`.
2. Выполнить стандартное преобразование Edwards -> Montgomery.
3. Полученное значение использовать как `X25519 public`.
На уровне математики используется стандартное отображение:
- `u = (1 + y) / (1 - y)`
где `y` берётся из публичной точки Ed25519, а результат `u` кодируется как публичный ключ `X25519`.
Замечания:
- нельзя получать `X25519 public` простым хэшированием `clientKey`;
- нельзя получать `X25519 public` "через приватный ключ на сервере";
- любой клиент должен уметь вывести `X25519 public` другого пользователя, имея только его `clientKey`.
## Что хранится в проекте
На текущем архитектурном уровне:
- в публичном профиле пользователя хранится только `clientKey` (`Ed25519 public`);
- приватная часть `clientKey` хранится только у владельца устройства;
- `X25519 private` и `X25519 public` считаются производными величинами и могут вычисляться по мере необходимости;
- отдельный `keyId` для DM пока не вводится.
Это означает:
- если в будущем пользователь сменит `clientKey`, старые DM должны будут быть перешифрованы;
- массовая перешифровка истории рассматривается как отдельная функция более позднего этапа;
- текущая архитектура должна оставлять возможность такой перешифровки, но не обязана реализовывать её прямо сейчас.
## Как это применяется в DM
Для личных сообщений SHiNE:
- входящая копия сообщения шифруется на `X25519 public`, полученный из `clientKey` получателя;
- исходящая копия сообщения шифруется на `X25519 public`, полученный из `clientKey` отправителя;
- расшифровать входящую копию может только владелец приватного ключа получателя;
- расшифровать исходящую копию может только владелец приватного ключа отправителя.
Следствия:
- `encryptedBody` у входящей и исходящей копии одного логического сообщения обычно разный;
- сервер не должен требовать побайтного совпадения ciphertext у пары;
- сервер не должен уметь расшифровывать содержимое DM;
- подпись и шифрование используют один корневой секрет пользователя, но разные стандартно выведенные представления ключа.
## Ограничения и компромиссы
Эта архитектура осознанно имеет следующие свойства:
- компрометация приватного `clientKey` одновременно даёт злоумышленнику возможность и подписывать от имени пользователя, и расшифровывать его личные сообщения;
- отдельное разделение ключа подписи и ключа шифрования пока не используется;
- простота пользовательской модели и минимизация числа публичных ключей считаются в проекте более важными, чем разделение этих ролей на текущем этапе.
Если позже проекту понадобится более строгая модель безопасности, допускается переход на отдельный публичный ключ шифрования, но только как отдельная версия протокола.
## Что запрещено
В проекте запрещено:
- придумывать собственное "детерминированное преобразование" `Ed25519 -> X25519`;
- хэшировать `clientKey` произвольным образом и считать результат `X25519 public`;
- использовать `Ed25519 public` напрямую как ключ ECDH;
- использовать сырой `Ed25519 seed` как `X25519 private` без стандартной процедуры;
- смешивать разные способы преобразования в разных клиентах.
## Требование к реализациям
Любая реализация SHiNE, которая:
- подписывает DM;
- шифрует DM;
- расшифровывает DM;
- проверяет совместимость личных сообщений между устройствами;
обязана использовать именно этот подход.
Если библиотека уже предоставляет готовые совместимые функции преобразования `Ed25519 -> X25519`, нужно использовать их вместо самодельной реализации.