Доработать вложенные файлы и архив проекта

This commit is contained in:
AidarKC
2026-09-11 14:33:21 +03:00
parent f9ae811702
commit ef068eca81
22 changed files with 3865 additions and 216 deletions
+124
View File
@@ -0,0 +1,124 @@
# Файлы и голосовые 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 строится как:
```text
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 записей:
```json
{
"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
После расшифровки:
```json
{
"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.
Технический блок отличается полями:
```text
kind=voice;dur=<milliseconds>
```
Получатель видит player с Play/Pause, прогрессом и длительностью. Аудио расшифровывается только на клиенте.