Document encrypted group attachment format and grant lifecycle
This commit is contained in:
1 parent
1896a19eba
commit
98f71735a0
2 files changed
+112
No files matched your search
@@ -0,0 +1,111 @@
|
||||
# Зашифрованные вложения группового чата
|
||||
|
||||
В desktop chatgui кнопка замка рядом со скрепкой включает шифрование следующего
|
||||
вложения или голосового сообщения. Выбор фиксируется вместе с адресатом до
|
||||
подготовки; переход в другую беседу сбрасывает переключатель. В DM используется
|
||||
существующее автоматическое E2E-шифрование лички.
|
||||
|
||||
Автор генерирует независимые случайные crypto_UUID и ключ AES-256 на каждую
|
||||
подготовку. Ключ записывается в локальную SQLite до шифрования. Готовый контейнер
|
||||
передаётся через обычные media_index/media_delivery. UUID транспортного медиа
|
||||
и crypto_UUID имеют разные назначения: первый идентифицирует сетевые блоки,
|
||||
второй — контейнер и ключ. Повтор доставки передаёт прежний контейнер.
|
||||
|
||||
## Подписанное сообщение и контейнер
|
||||
|
||||
В подписанном сообщении `ct=application/x-utun-encrypted`, базовое имя тела —
|
||||
`<crypto_UUID:32hex>.uenc`. Остальные поля manifest описывают шифротекст:
|
||||
размер, SHA256 всего контейнера, UUID и подписи блоков. Ключ, исходное имя,
|
||||
MIME, waveform и остальные медиаметаданные в историю канала не записываются.
|
||||
|
||||
Контейнер:
|
||||
|
||||
```text
|
||||
magic "UTENC001":8
|
||||
crypto_UUID:16
|
||||
plaintext_size:8BE
|
||||
encrypted_metadata_size:2BE
|
||||
SHA256(encrypted_file_body):32
|
||||
encrypted_metadata:variable
|
||||
encrypted_file_body:variable
|
||||
```
|
||||
|
||||
Метаданные содержат MIME в 32 байтах и сериализованный `attachment_info`.
|
||||
Каждая часть шифруется AES-256-CCM с тегом 16 байт и nonce 13 байт.
|
||||
Nonce метаданных использует `part=UINT64_MAX`; файл — порции 32768 байт
|
||||
с номерами от нуля. Общие примитивы находятся в `media_async/media_crypto`;
|
||||
формат существующего DM при их выделении не менялся.
|
||||
|
||||
Получатель проверяет подписанный SHA256 полного контейнера, UUID, tag метаданных,
|
||||
все tags файла, точный размер и hash тела. Результат пишется во временный файл
|
||||
и публикуется после успешной проверки и durable rename. Все длительные файловые
|
||||
операции и криптография выполняются в media_async.
|
||||
|
||||
## Хранение и разрешения
|
||||
|
||||
Локальные таблицы не регистрируются в db_sync или Merkle:
|
||||
|
||||
- `attachment_keys`: ключ автора/получателя, связь с общим message_id,
|
||||
crypto_UUID, hash и пути двух копий, расшифрованные метаданные и состояние.
|
||||
- `attachment_key_requests`: один запрос на `(channel,message_id,peer)`,
|
||||
направление, решение и дата создания.
|
||||
- `attachment_key_outbox`: команда, адресат и время следующей попытки.
|
||||
|
||||
Шифротекст хранится в `media/<channel>/<crypto_UUID>.uenc`, локальная открытая
|
||||
копия — в `decrypted/<channel>/<crypto_UUID>.file`. В media_files и объявлениях
|
||||
держателей остаётся только шифротекст, включая backfill после restart.
|
||||
Квота медиакеша учитывает обе копии; очистка кеша не удаляет ключ. При повторном
|
||||
открытии удалённая открытая копия восстанавливается с сохранённым ключом.
|
||||
|
||||
После полной проверенной загрузки обычный клиент автоматически запрашивает
|
||||
ключ автора. Фоновая копия узла с подписанной ролью `storage=yes` запрос не
|
||||
создаёт; ручная пользовательская загрузка на таком узле запрашивает доступ.
|
||||
Сетевая классификация `peers.storage` для этого не используется.
|
||||
|
||||
В шапке канала кнопка «Запросы расшифровки» показывает число запросов без решения.
|
||||
Список содержит участника, вложение, дату и отдельные кнопки выдачи/отказа.
|
||||
Двойной клик переводит к исходному сообщению. После выдачи видны ожидание доставки
|
||||
и подтверждённая доставка ключа; отказ также сохраняется.
|
||||
|
||||
## Адресный протокол
|
||||
|
||||
Router-сервис `ETCP_RT_ID_ATTACHMENT_KEYS=0x40`. Каждый пакет обязан иметь
|
||||
`ROUTER_FLAG_SIGNED | ROUTER_FLAG_ENCRYPTED`; промежуточные узлы ключа не видят.
|
||||
Протокол не создаёт личную беседу и не публикует служебные сообщения в канал.
|
||||
|
||||
```text
|
||||
magic "AK01":4 | command:1 | message_id:32 | crypto_UUID:16 | cipher_hash:32
|
||||
GRANT дополнительно содержит key:32
|
||||
|
||||
REQUEST=1, REQUEST_ACK=2, GRANT=3, DENY=4, RESULT_ACK=5
|
||||
```
|
||||
|
||||
Автор принимает запрос только на собственное опубликованное сообщение с
|
||||
сохранённым ключом. Участие запрашивающего проверяется по полному подписанному
|
||||
мемберу, join и дереву приглашений; удалённые мемберы отклоняются. Проверка
|
||||
повторяется при решении и перед каждой отправкой GRANT. Получатель принимает
|
||||
ответ только от автора, по своему существующему запросу и точной идентичности
|
||||
контейнера. Конфликтующие ключи и результаты отклоняются.
|
||||
|
||||
REQUEST_ACK отправляется после сохранения запроса. GRANT/DENY появляются
|
||||
в outbox только после сохранения решения. Полученный ключ сохраняется до
|
||||
RESULT_ACK и до запуска расшифровки. Повторы возвращают прежнее решение и не
|
||||
создают новых строк. Таймер обрабатывает ограниченную порцию очереди с
|
||||
backpressure и повтором через 5 секунд; restart продолжает очереди.
|
||||
|
||||
Для доставки нужен живой маршрут обеих сторон: офлайн-mailbox этого сервиса
|
||||
не реализован. Запрос ждёт появления автора; решение ждёт появления получателя.
|
||||
Уже выданный ключ невозможно отозвать у получателя. Защита содержимого не
|
||||
скрывает автора, канал, время и размер шифротекста. Автоматическая публикация
|
||||
транскрипции зашифрованного голосового в канал не запускается.
|
||||
|
||||
## Проверки
|
||||
|
||||
`test_encrypted_file`: границы порций, пустой файл, metadata roundtrip,
|
||||
неверные ключ/автор/UUID/hash, подмена метаданных с пересчитанным внешним hash,
|
||||
лишние байты и отсутствие частичного plaintext после отказа.
|
||||
|
||||
`test_encrypted_attachment`: реальные A–S–B/C, скачивание с S при выключенном A,
|
||||
перезапуски автора с запросами и получателя с ключом, выдача B и отказ C,
|
||||
отсутствие ключа на S/C, отрицательные control-пакеты, повтор GRANT,
|
||||
восстановление удалённого plaintext без нового согласия и удаление канала.
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "../transport_layer/secure_channel.h"
|
||||
#include "../transport_layer/etcp.h"
|
||||
#include "../media_delivery/file_transfer.h"
|
||||
#include "../media_async/encrypted_file.h"
|
||||
|
||||
enum dm_file_work { DM_FILE_PREPARE, DM_FILE_RECEIVE, DM_FILE_CUSTODY, DM_FILE_DELETE };
|
||||
struct dm_file_job;
|
||||
|
||||
Reference in new issue
Block a user