5.2 KiB
Файлы и голосовые DM v2: chunked AES-GCM + BitTorrent v2 hashes
Цели
DM file v2 убирает ограничение размера исходного файла, не требует держать файл целиком в RAM и сохраняет серверную модель «сервер видит только ciphertext».
Основные свойства:
- plaintext режется на куски по
1 MiB; - каждый кусок независимо шифруется
AES-256-GCMодним случайным ключом файла, но с уникальным 96-bit IV; - каждый ciphertext-кусок хранится как immutable объект
Base58(SHA-256(ciphertext)); - список кусков хранится в зашифрованных страницах манифеста по 256 записей;
- корневой манифест тоже зашифрован и content-addressed;
- E2EE DM содержит только ссылку на корневой манифест, AES-key, IV-prefix и пользовательские метаданные;
- один DM может содержать до 10
<S:file;v=2...>блоков; kind=voiceиспользует тот же формат хранения и отдельный UI проигрывателя.
IV и domain separation
На один файл создаётся случайный 4-byte ivPrefix.
12-byte IV строится как:
ivPrefix[4] || uint64_be(token)
Диапазоны token разделены:
- chunks:
0 .. 2^63-1; - manifest pages:
2^63 + pageIndex; - root manifest:
0xffffffffffffffff.
AAD также содержит домен (chunk, page, root), prefix и индекс. Поэтому перестановка ciphertext-кусков не проходит AES-GCM authentication.
Manifest pages
Каждая страница после расшифровки содержит до 256 записей:
{
"v": 2,
"page": 0,
"chunks": [
{
"i": 0,
"id": "Base58(SHA-256(ciphertext))",
"ps": 1048576,
"es": 1048592,
"ph": "BitTorrent-v2-piece-hash-base64url"
}
]
}
Страницы сами AES-GCM зашифрованы и загружаются в /dm-files/{id}.
Root manifest
После расшифровки:
{
"v": 2,
"scheme": "SHINE-DM-CHUNKED-AES-256-GCM",
"chunkSize": 1048576,
"fileSize": 123456789,
"chunkCount": 118,
"pages": [{"id":"...","count":118,"encryptedSize":12345}],
"torrent": {
"metaVersion": 2,
"blockLength": 16384,
"pieceLength": 1048576,
"piecesRoot": "...",
"infoHash": "..."
}
}
Имя файла и MIME в manifest не пишутся. Они остаются внутри E2EE DM.
BitTorrent v2 совместимость
Хеширование соответствует BEP 52:
- базовый hash block:
16 KiB; - SHA-256;
- piece length:
1 MiB; - Merkle padding leaf = 32 zero bytes;
pieces rootвычисляется по правилам BitTorrent v2;infoHash=SHA-256(bencode(info dictionary));- piece-layer hash каждого 1-MiB куска хранится в зашифрованной manifest page.
Из name, size, piecesRoot и списка ph можно построить tracker-less .torrent v2 без повторного хеширования исходного файла.
Это совместимость метаданных и проверки контента. HTTP /dm-files пока не является BitTorrent peer transport: для настоящего P2P потребуется отдельный seeding/peer слой.
Скачивание
Получатель:
- получает и проверяет ciphertext root manifest по Base58(SHA-256);
- расшифровывает root manifest;
- по очереди получает manifest pages;
- получает каждый chunk с сервера отправителя;
- проверяет content address ciphertext;
- расшифровывает chunk локально;
- проверяет BitTorrent-v2 piece hash;
- пишет plaintext на диск;
- в конце проверяет итоговый
pieces root.
В браузерах с File System Access API plaintext пишется на диск по частям и целиком в RAM не собирается. В остальных браузерах остаётся Blob fallback без искусственного лимита размера, но фактический предел зависит от памяти браузера.
Голосовые
MediaRecorder пишет Opus/WebM, Opus/Ogg или поддерживаемый браузером audio MIME. После завершения запись проходит тот же DM file v2 pipeline.
Технический блок отличается полями:
kind=voice;dur=<milliseconds>
Получатель видит player с Play/Pause, прогрессом и длительностью. Аудио расшифровывается только на клиенте.