9.6 KiB
Технические вставки внутри plaintext DM v1
Статус документа
Этот файл описывает внутренний формат технических вставок, которые находятся внутри уже расшифрованного plaintext DM.
Важно:
- это не отдельный серверный DM-envelope;
- это часть plaintext контентного DM после E2EE-расшифровки;
- сервер не обязан знать этот формат;
- клиент может использовать эти вставки для специального UI-рендера.
1. Общий принцип
Обычное сообщение по-прежнему остаётся обычным текстом.
Если в начале plaintext стоит один или несколько специальных блоков формата:
<S:...>
то клиент трактует их как технические вставки.
Техническими считаются только блоки, которые:
- стоят строго в начале plaintext;
- начинаются с префикса
<S:или legacy-префикса<SHiNE:; - заканчиваются первым символом
>.
Если текст не начинается с <S: или <SHiNE:, никакие технические вставки не ищутся.
2. Правило скрытия
Все корректно распознанные блоки <S:...> или <SHiNE:...> в начале plaintext:
- не показываются пользователю как сырой текст;
- используются клиентом для UI-логики;
- неизвестные будущие типы тоже скрываются, если они распознаны как корректный SHiNE-блок.
Если блок битый и не закрыт символом >, он не считается техническим и сообщение показывается как обычный текст целиком.
3. Защита от случайного пользовательского ввода
Перед отправкой обычного текстового сообщения клиент обязан проверить:
- если пользовательский текст начинается с
<S:или<SHiNE:
то клиент автоматически превращает начало в:
< SHiNE:
Такой текст уже не считается техническим блоком и должен отображаться как обычное сообщение.
4. Формат блока
Общий вид:
<S:kind;key=value;key=value;...>
Правила:
kind— ASCII-идентификатор типа вставки;- параметры отделяются
;; - ключ и значение отделяются
=; - значения обычных v1-вставок не экранируются; для
fileполяurl,name,mimeкодируются через percent-encoding (encodeURIComponent), чтобы;,=и>внутри метаданных не ломали блок; - канонический новый префикс:
<S:; - legacy-префикс
<SHiNE:продолжает поддерживаться при чтении.
5. Тип reply
Формат:
<S:reply;v=1;id=user1|user2|1720612345678|77>Текст ответа
Где поле id — это логический идентификатор сообщения:
fromLogin|toLogin|timeMs|nonce
Правила:
- этот блок должен стоять в начале plaintext;
- после него может идти обычный текст ответа;
- официальный UI формирует такой блок при отправке ответа через пункт
Ответитьв меню сообщения; - если клиент не находит сообщение, на которое ссылается
reply, он просто не показывает reply-preview; - в таком случае само сообщение отображается как обычный текст без блока
<S:reply...>.
6. Тип call
Успешный звонок
<S:call;v=1;status=completed;duration=367>
Где:
duration— длительность разговора в секундах.
Неуспешный звонок
<S:call;v=1;status=failed;reason=offline>
Допустимые причины в v1:
offlineno_answerconnect_failedbusydeclined
Правила:
- call-блок в v1 должен содержать только техническую запись звонка;
- пользовательский текст после такого блока в штатной логике не предполагается;
- UI может рисовать такие сообщения отдельным специальным стилем.
- официальный UI не отправляет call-summary, если от старта исходящего звонка до его завершения прошло меньше
5секунд.
7. Тип file
Формат v1:
<S:file;v=1;id=BASE58_SHA256;url=ENCODED_URL;key=BASE64URL_AES_KEY;iv=BASE64URL_IV;name=ENCODED_NAME;mime=ENCODED_MIME;size=123;encsize=139>📎 example.pdf
Поля:
id—Base58(SHA-256(ciphertext));url— абсолютный URL ciphertext на access-сервере отправителя, percent-encoded;key— случайный 32-byte AES-256 key в Base64URL;iv— случайный 12-byte AES-GCM IV в Base64URL;name— исходное безопасно нормализованное имя файла, percent-encoded;mime— исходный MIME, percent-encoded;size— размер plaintext в байтах;encsize— размер ciphertext в байтах.
key, iv и метаданные безопасно находятся здесь только потому, что весь plaintext DM затем шифруется существующим E2EE-механизмом. Они не передаются файловому HTTP endpoint отдельно.
Текст 📎 example.pdf после блока служит fallback для старого клиента. Новый официальный UI вместо него рисует карточку файла и кнопку «Скачать».
Перед локальной AES-GCM-расшифровкой клиент обязан повторно вычислить SHA-256 скачанного ciphertext и сравнить Base58 с id.
8. Поведение официального UI
Официальный UI SHiNE в v1:
- скрывает все корректные
<S:...>и legacy<SHiNE:...>блоки в начале plaintext; - для
callстроит специальный человекочитаемый текст:Звонок: M:SSЗвонок: H:MM:SSЗвонил, но недозвонился: ...
- для
replyскрывает сам блок и показывает только текст ответа; - для
fileскрывает fallback-текст и показывает карточку вложения с локальным download/decrypt; - если исходное reply-сообщение не найдено, reply-preview не показывается.
9. Совместимость
Так как это часть plaintext, а не часть серверного envelope:
- сервер не обязан понимать этот формат;
- будущие клиенты могут добавлять новые
kind; - клиенты, которые распознают SHiNE-вставки, должны скрывать неизвестные блоки целиком, если они стоят в начале и корректно закрыты.
10. Расширение file v2
Клиент обязан продолжать читать v=1. Новые chunked-вложения отправляются так:
<S:file;v=2;id=ROOT_MANIFEST_ID;url=ENCODED_URL;key=BASE64URL_AES_KEY;ivp=BASE64URL_4BYTE_PREFIX;name=ENCODED_NAME;mime=ENCODED_MIME;size=123;encsize=456;chunk=1048576;chunks=7;th=TORRENT_V2_INFOHASH;pr=TORRENT_V2_PIECES_ROOT;kind=file;dur=0>
Дополнительные поля v2:
id/urlуказывают не на весь файл, а на зашифрованный root manifest;ivp— случайный 4-byte IV prefix, из которого детерминированно строятся уникальные IV chunks/pages/root;chunk— plaintext chunk size, сейчас1048576;chunks— число частей;th— BitTorrent v2 SHA-256 infohash;pr— BitTorrent v2 pieces root;kind=file|voice;dur— длительность voice в миллисекундах, для обычного файла0.
В одном plaintext DM разрешено несколько последовательных <S:file...> блоков. Официальный UI собирает их в один список вложений и скрывает fallback-текст.
Подробный формат chunk encryption, manifest pages и BitTorrent v2 hashing: Файлы_DM_v2.md.