Files

5.2 KiB
Raw Permalink Blame History

Файлы и голосовые 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 слой.

Скачивание

Получатель:

  1. получает и проверяет ciphertext root manifest по Base58(SHA-256);
  2. расшифровывает root manifest;
  3. по очереди получает manifest pages;
  4. получает каждый chunk с сервера отправителя;
  5. проверяет content address ciphertext;
  6. расшифровывает chunk локально;
  7. проверяет BitTorrent-v2 piece hash;
  8. пишет plaintext на диск;
  9. в конце проверяет итоговый 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, прогрессом и длительностью. Аудио расшифровывается только на клиенте.