SHA256
Доработать вложенные файлы и архив проекта
This commit is contained in:
@@ -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, прогрессом и длительностью. Аудио расшифровывается только на клиенте.
|
||||
Reference in New Issue
Block a user