# Формат доказів

Canonical page: https://phishdestroy.github.io/TgWitness/uk/docs/reference/evidence/

Вміст підписаного ZIP-архіву, типи записів, підтвердження обробки, профілі користувачів, зовнішні копії та офлайн-перевірка.

Спостереження не є висновком. Ідентифікатор відправника не є реальним ідентифікатором особи. Джерелом є оновлення сервісу, видиме для користувача; Telegram не підписує криптографічно ці збережені записи для сторонніх слідчих.

## Що підписується

| Матеріал | Захист | Що слід зберігати |
| --- | --- | --- |
| Кожен запис у журналі, включаючи оперативні події | Ланцюжок SHA-256 та підпис Ed25519 для кожної події | Оригінальний `events.jsonl` — відбиток надійного ключа та збережений контрольний пункт |
| Вміст ZIP-архіву з доказами | `manifest.sig` підписує канонічний маніфест, у якому перелічено хеші файлів вмісту | Повний ZIP-архів, що містить маніфест та підпис |
| Збережені вкладення | SHA-256 та кількість байтів у підписаній події `media.saved`; хеш файлу у підписаному маніфесті ZIP | Оригінальні байти всередині перевіреного ZIP-файлу |
| Checkpoint | Підпис Ed25519, що охоплює довжину журналу, хеш заголовка, відкритий ключ та час створення | Незалежна копія, збережена до внесення спірного зміни |
| Квитанція про виконання | Окремо підписані вимірювання колектора; також включені до підписаної події `process.started` | Квитанція та відповідний журнал; показники наводяться за власними даними |
| Автономний файл у форматі CSV або HTML | Відсутність окремого криптографічного підпису; самих хеш-значень подій недостатньо | Для перевірки цілісності використовуйте точну копію, що міститься у перевіреному ZIP-файлі |
| Знімки екрана, роздруківки або PDF-файли, створені браузером | Відсутність підпису TgWitness на похідному матеріалі | Зберігайте оригінальний перевірений пакет і задокументуйте, як було створено похідний пакет |

Колектор перевіряє підписи журналу перед генерацією кожного формату експорту. Ця перевірка джерела не створює підпису на окремо завантаженому файлі у форматі CSV або HTML. Дійсний підпис події у форматі JSONL також не надає байти вкладення та не підтверджує самостійно, що весь зафіксований журнал був наданий.

## Передача розслідування

1. Зафіксуйте у своїх нотатках щодо справи номер справи, оператора, числовий ідентифікатор облікового запису Telegram, вибрані джерела, політику збору даних та період збору. TgWitness зберігає спостереження; призначення справ та записи про зберігання даних управляються поза межами додатка. Підтвердьте налаштування збору даних, що планується, за допомогою [процедури прийняття в режимі реального часу](https://github.com/phishdestroy/TgWitness/blob/main/docs/internal/LIVE_ACCEPTANCE.md).
2. Зберігайте відбиток відкритого ключа робочого простору незалежно, перш ніж покладатися на його експортні дані. Зберігайте підписані контрольні точки поза межами колектора під час збору даних. Фіксуйте переривання та обмеження видимості облікового запису.
3. Експортуйте **підписаний ZIP**-файл і збережіть підписану квитанцію про виконання. Використовуйте контрольну точку на межі експортованого журналу або перед нею. Пізніша контрольна точка може правильно відхилити раніше експортований файл як занадто короткий. Експорт включає повний журнал робочого простору, незалежно від фільтрів панелі; перегляньте матеріал перед тим, як поділитися ним.
4. Попросіть одержувача перевірити ZIP-файл за допомогою надійної копії верифікатора та незалежно наданого відбитка/контрольної точки. З надійного робочого середовища з активним середовищем Python:

   ```
   python -m tgwitness.verify evidence.zip --fingerprint TRUSTED_PUBLIC_KEY_SHA256 --checkpoint retained-checkpoint.json
   ```

   У разі успішного виконання функції видаються звіти `key_pinned: true` та `checkpoint_checked: true` , якщо обидва вхідні параметри надані та прийняті. Збережіть результат перевірки.

   ```
   For a handover requiring the final exported record to be witnessed, use
   `--witness witness.zip --witness-fingerprint TRUSTED_WITNESS_SHA256 --require-witness`.
   This refuses an export with an unwitnessed tail. See the
   [witness handover procedure](WITNESS.md#require-a-witnessed-export-boundary).
   ```

   1. Зафіксуйте SHA-256 архіву, межі експорту, дату передачі, відправника та одержувача у вашому протоколі зберігання. Зберігайте оригінальний пакет у незмінному вигляді та працюйте з копіями. Надавайте файли доказів, а не приватний каталог даних програми, який також містить сесії облікових записів та секретні ключі підпису. Для передачі експорт можна помістити в ZIP-архів, захищений паролем за алгоритмом AES-256. Пароль захищає лише конфіденційність і не зберігається; одержувач розпаковує архів і перевіряє підписаний експорт усередині, який залишається незмінним.
   2. Якщо потрібна незалежна прив’язка до часу, дотримуйтесь [процедури RFC 3161](https://phishdestroy.github.io/TgWitness/uk/docs/reference/timestamp/) та збережіть запит, контрольну точку, інформацію про довірений центр сертифікації та перевірену відповідь центру сертифікації. Сам по собі запит не забезпечує отримання часової мітки.

Успішна верифікація підтверджує цілісність щодо надійного ключа-збирача та збережених меж. Джерельна достовірність, повна доставка повідомлень Telegram, надійний час та юридична допустимість вимагають окремих доказів. Ця версія ще не пройшла реальну кваліфікацію Telegram або незалежну криміналістичну/безпекову оцінку.

Скопійований ключ колектора може підписати сфабрикований журнал-замінник. Перевірка щодо незалежно збереженого контрольного пункту або запису свідка виявляє змінений попередній префікс; сама лише перевірка підпису цього зробити не може. «Свідок» фіксує те, що представив «колектор», тому він не встановлює самостійно правдивість записів, сфабрикованих до їхнього першого зовнішнього контрольного пункту. Звіти про розгортання в хмарі перевіряють конфігурацію та вимірювання, повідомлені «колектором»; вони не підтверджують, що приватний ключ ніколи не був скопійований під час початкового запуску.

Повідомлення, що надсилаються через Apprise, містять назви робочих просторів та чатів, а також їхню кількість; короткі уривки видалених та відредагованих повідомлень додаються лише тоді, коли оператор увімкне цю функцію (за замовчуванням вона вимкнена). Вони є сигналом для зручності, а не доказом: доставка не гарантується, і лише журнал та його підписані експортовані файли фіксують те, що було зафіксовано. За наявності проксі робочого простору сповіщення проходять тим самим маршрутом; сервіси, які не можуть ним скористатися, а також маршрути MTProxy, відхиляються.

## Канонізація подій

Вхідні дані хешу складаються саме з таких полів:

```
seq, observed_at, source_at, kind, chat_id, message_id, actor_id, payload, previous_hash
```

Серіалізація за допомогою JSON, сумісного з Python, у кодуванні UTF-8, із відсортованими ключами об’єктів, без ASCII- ескейпу, без незначущих пробілів та без NaN/infinity:

```
json.dumps(value, ensure_ascii=False, sort_keys=True,
           separators=(",", ":"), allow_nan=False).encode("utf-8")
```

SHA-256 цих байтів стає хешем події. Підпис генерується за алгоритмом Ed25519 над 32 необробленими байтами хешу, а потім кодується у форматі base64. Перший previous_hash складається з 64 нулів. Порядкові номери починаються з 1 і збільшуються на 1. Усі події, включаючи оперативні події, об’єднуються в один ланцюжок. Придушення дублювання доставки не відбувається: повторювані спостереження є окремими підтвердженнями отримання, а не твердженням про окремі дії Telegram.

Необроблені дати Telegram є рядками у форматі ISO; бінарні поля перетворюються на об’єкти, що містять bytes_base64. Текст повідомлення, об’єкти, дескриптори медіа, метадані відповідей, реакції та дії сервісу зберігаються в необроблених метаданих повідомлення, якщо вони надані.

observed_at — це час за UTC на годиннику колектора. source_at — це дата з Telegram, якщо вона доступна. Повідомлення про видалення не містять точного часу видалення, тому source_at залишається нульовим. Порядок надходження даних не обов’язково збігається з порядком подій у джерелі. У знімках історії використовуються старіші дати джерела, але поточний час спостереження.

## Семантика учасників

message.new actor_id — це ідентифікатор відправника повідомлення. У message.edited не вказується редактор; payload.sender_id фіксує відправника спостережуваного повідомлення. Видалення actor_id залишається нульовим. Повідомлення служби членства зберігають відправника та тип дії, не перетворюючи кожне видалення на добровільний вихід. Необроблені оновлення учасників зберігають actor_id у тому вигляді, в якому він надано. Знімки реакцій не вигадують суб’єкта зміни.

Повідомлення про видалення для приватних чатів та звичайних груп містять лише ідентифікатори повідомлень. Ці чати мають спільну послідовність ідентифікаторів для всього облікового запису, тому ідентифікатор ідентифікує одне повідомлення цього облікового запису. Якщо повідомлення з цим ідентифікатором було раніше збережено в цьому журналі, його видалення фіксується в цьому чаті як message.deleted із методом payload.chat_resolution у назві. Ідентифікатори, які ніколи не зберігалися, зберігаються як deletion.unscoped без chat_id; ніщо не припускається. Канали та супергрупи нумерують повідомлення за кожним чатом і ніколи не зіставляються таким чином. Якщо видалене повідомлення було надіслано обстежуваним обліковим записом, payload.deleted_message_was_outgoing має значення true; інформація про того, хто здійснив видалення, все ще не надається.

## Охоплення: безперервність, прогалини та неспостережувані ідентифікатори

Кожен підписаний ZIP-файл містить `coverage.json`, HTML-звіт відкривається з тим самим резюме, а `verify.py` перераховує його на основі `events.jsonl`, замість того щоб довіряти файлу:

- **Безперервне спостереження від X до Y.** Період починається з `observation.started` і закінчується паузою, зупинкою, втратою з’єднання з Telegram, виходом із системи, запуском процесу без послідовної зупинки або більше ніж трьома хвилинами без жодних записів під час спостереження (сигнали активності записуються щохвилини, тому тиша означає, що процес зупинився, завис або комп’ютер перейшов у режим сну). Коли інтервал сигналів активності змінюється в **розділі «Параметри»**, кожен сигнал фіксує це, і допустима тривалість тиші становить три інтервали, але ніколи менше трьох хвилин.
- У списку **прогалин** вказано час між періодами та причину. Прогалина, яка все ще залишається відкритою на момент останнього запису, позначається як `not_resumed_by_last_record`.
- **Ідентифікатори повідомлень не фіксуються.** Для каналів та супергруп, у яких повідомлення нумеруються за чатом, ідентифікатори, що відсутні між зафіксованими, вказуються у вигляді діапазонів. Вони позначаються як «не*зафіксовані*» — видалені, ніколи не доставлені або поза збереженою історією — але ніколи як «видалені». Приватні чати та звичайні групи використовують спільну послідовність ідентифікаторів для всього облікового запису, тому пропуски там нічого не означають і не вказуються.
- Повідомлення про прогалини на рівні чату (наприклад, запит Telegram щодо відновлення втрачених даних в одному каналі) враховуються як примітки; вони не переривають загального охоплення.

Опис охоплення пояснює, що саме зафіксував цей збирач даних. Це не є доказом того, що Telegram доставив усі оновлення протягом безперервного періоду.

## Виробник

Підписаний маніфест ZIP-файлів з доказами, експортованих чатів, журналів свідків та файлів профілів осіб містить `producer` : програмне забезпечення, версію, проект (PhishDestroy), домашню сторінку та репозиторій вихідного коду. У звіті у форматі HTML відображається той самий рядок, а `verify.py` виводить його як частину перевіреної інформації. Архіви, створені до появи цього поля, перевіряються так само, як і раніше, і в звіті вказується, що виробник не зазначений.

## Профілі користувачів

Файл профілю (`tgwitness-dossier-v1`, JSON) формується на запит на основі записів, що вже містяться у журналі для одного чату; нічого не отримується з Telegram, а вміст профілю ніколи не записується до журналу (увімкнення та вимкнення профілів фіксується як `dossier.changed`). Для кожної особи, повідомлення якої збережено, вказуються ім’я та історія імен користувача, позначки Telegram (бот, шахрайство, фейк, преміум, підтверджений, обмежений, видалений) станом на момент збереження, ідентифікатори профілю фото-ідентифікатори, SHA-256 та перцепційний хеш (pHash, сумісний з `imagehash.phash`) аватара з локального кешу аватарів із подібними аватарами, активність (перше та останнє повідомлення, кількість, відредаговані та видалені публікації, повідомлення за годинами за UTC), хто кому відповідає, інші чати у цьому журналі, де ця особа писала, та згадані гаманці (BTC, EVM, TRON, TON, LTC, DOGE, XMR, SOL; адреси Base58Check перевірені за контрольною сумою), номери телефонів, імена користувачів та посилання, включаючи посилання, приховані за текстом.

Кожен факт містить посилання на записи журналу (`seq`), з яких він походить, а `record_hashes` містить хеш кожного запису, на який є посилання. Заява підписана ключем робочого простору:

```
python verify.py tgwitness-dossier-CHAT.json --fingerprint TRUSTED_SHA256 --evidence tgwitness-evidence.zip
```

Перевіряє підпис, перевіряє сам експорт доказів (підпис, ланцюжок хеш-кодів та файли) та наявність кожного згаданого запису без змін у цьому експорті з того самого робочого простору. Хеш-коди аватарів позначені як такі, що походять із локального кешу (а не із записів журналу). Згадки є збігами за шаблоном — це підказки для перевірки, а не перевірені факти. Профілі вмикаються для кожного чату окремо, можуть експортуватися у вигляді ZIP-архіву з шифруванням AES-256 та паролем, і видаляються при вимкненні або коли чат видаляється з джерел.

## Незалежний свідок

Журнал свідчень (`tgwitness-witness-v1`) та публікації в його каналі перевіряються на відповідність експорту доказів за допомогою `verify.py --witness … --channel …`; див. [незалежний свідок](https://phishdestroy.github.io/TgWitness/uk/docs/reference/witness/).

## Маніфест та контрольні точки

Файл manifest.json містить формат, час експорту, відкритий ключ, відбиток, довжину журналу, хеш заголовка, область дії, виробника, обмеження та SHA-256 кожного елемента вмісту, крім самих `manifest.json` та `manifest.sig`. manifest.sig — це підпис Ed25519 у кодуванні base64 над канонічним JSON-маніфестом. Верифікатор відхиляє несподівані/відсутні елементи, дублікати імен, пошкоджені файли, порушену послідовність/ланцюг, недійсні підписи та невідповідність наданій контрольній точці. Він ніколи не витягує шляхи до архівів. Він обмежує загальний заявлений розмір у нестисненому вигляді до 4 ГіБ; файл `--max-gib N` дозволяє створювати більші архіви за допомогою підвищеного обмеження **параметрів**.

Отримайте код верифікатора з надійного джерела або перевіреної незалежної копії. Верифікатор у ненадійному архіві є виконуваним матеріалом, наданим автором цього архіву; його наявність є зручністю для перенесення, а не незалежним джерелом довіри.

Контрольна точка підписує заяву, що містить відкритий ключ, довжину журналу, хеш заголовка та час створення. Верифікатор може перевірити, чи містить пізніший архів той самий префікс. Зберігайте контрольну точку поза межами колектора перед спірною зміною: зловмисник, який контролює і архів, і контрольну точку, може замінити обидва.

Дійсний підпис від незакріпленого ключа підтверджує лише внутрішню узгодженість. Навіть закріплений ключ не може довести достовірність вихідного вмісту, якщо збирач даних або ключ були скомпрометовані до початку запису. Зовнішнє маркування часом та надійне фіксування є окремими вимогами. TgWitness не гарантує придатності до судового розгляду або криміналістичної сертифікації.

## Вкладення

Файли ідентифікуються за допомогою SHA-256 у каталозі media/. Подія media.saved пов’язує хеш та довжину у байтах із конкретною подією повідомлення. Необроблені імена файлів є метаданими, а не локальними шляхами. Байти мультимедіа завантажуються асинхронно, тому негайне видалення може перешкодити їхньому збереженню. У результатах відображаються випадки вимкненого збору, файли надмірного розміру або невідомого розміру, помилки завантаження та перервані завантаження. Збій процесу може залишити неповні файли без посилань; вони не включаються в експортні дані.

Експортер у форматі ZIP перевіряє збережені медіафайли на відповідність їхнім іменам. Експорт у форматах JSONL, CSV та HTML перевіряє сигнатури подій, але не передає байти медіафайлів. Похідні звіти є допоміжними; файл events.jsonl є авторитетним представленням подій.

## Довготривале збирання та обробка квитанцій (0.2)

Медіа-завдання та фіксація події повідомлення відбуваються в одній транзакції. Завдання зберігає оригінальний дескриптор повідомлення TL; підписане спостереження пов’язує його SHA-256, обліковий запис та політику розміру. Функція відновлення перевіряє цю прив’язку перед завантаженням. Вона ніколи не завантажує поточне повідомлення і непомітно замінює його на новіше вкладення. Перервані завдання повторюються, тоді як постійна помилка залишається явним результатом.

Кожна робоча область має власний журнал та ідентифікатор підпису. Подія process.started містить ідентифікатор запуску, час за локальним годинником, версії залежностей, хеші вихідних файлів, початковий обліковий запис/область дії, а також налаштування збору даних. Сигнали Heartbeat містять ідентифікатор запуску, монотонний час та стан черги. Квитанцію про процес також можна завантажити як окремо підписану заяву. Вимірювання файлів є самозвітами і не є надійним апаратним підтвердженням.

Автоматичні контрольні точки зберігаються локально під час запуску, кожні п’ять хвилин під час спостереження (**Параметри → Підписана контрольна точка кожні**) та при упорядкованому зупиненні. Під час перевірки при запуску та експорту журнал порівнюється з останньою збереженою локальною контрольною точкою. Це дозволяє виявити відкат бази даних, якщо контрольна точка збереглася; оператор, який контролює обидва елементи, все одно може замінити їх обидва. Незалежне зберігання та зовнішні надійні часові мітки залишаються окремими вимогами.

Після перезапуску відновлюється збережена позиція оновлення Telegram (`catch_up_from` у `observation.started`), і надсилається запит на пропущені оновлення. Періодична повторна перевірка фіксує `message.recovered` для пропущених повідомлень та `message.missing_on_recheck`, коли Telegram більше не повертає збережене повідомлення; останнє не є повідомленням про видалення і не вказує ні імені особи, яка видалила повідомлення, ні часу.

### Зовнішнє копіювання та відновлення (S3)

Зовнішня копія (Експорт, за замовчуванням вимкнено) завантажує сегменти журналу, збережені файли, контрольні точки з їхніми часовими мітками та готові експортовані чати до сховища, сумісного з S3, зашифровані за допомогою AES-256-GCM перед завантаженням за допомогою ключа, що зберігається у сховищі робочого простору; застосовується функція збереження Object Lock, якщо контейнер її підтримує. Без завантаженого ключа копію неможливо прочитати. Відновлення та перевірка за допомогою `python -m tgwitness.replica restore --key-file KEY.json --out DIR`.

Для відновлення потрібен порожній звичайний каталог. Завантажені та розшифровані об’єкти залишаються у приватному тимчасовому сховищі, доки не буде перевірено весь журнал та кожну збережену контрольну точку. Небезпечні шляхи до об’єктів, непослідовні діапазони сегментів та журнали, коротші за контрольну точку, відхиляються; у разі невдалої перевірки відновлені файли не публікуються. Перевірки збереження порівнюють повернутий режим Object Lock та дату з кінцевим терміном, надісланим під час завантаження (допуск на округлення — одна секунда). Термін, що минув, недійсний або скорочений, не вважається перевіреним. Статус повідомляє про об’єкт, з якого взято зразок, збереження якого було зчитано для цього циклу завантаження.

Прогрес копіювання обмежується місцем призначення та ключем шифрування. Зміна кінцевої точки, контейнера або префікса запускає повне копіювання локально збереженого журналу, мультимедіа та контрольних точок у це місце призначення. Старий прогрес, що не входить у сферу дії, зберігається, але відтворюється лише один раз після оновлення. Історичні експорти чатів, які вже видалені з локальної папки «Вихідні», залишаються у своєму попередньому місці призначення.

Відсутність або пошкодження локальних носіїв фіксується як помилка циклу і курсор залишається перед цим місцем, щоб файл було перепробувано під час наступного циклу; файли, контрольні точки та експортовані дані, що йдуть за ним, все одно копіюються. Під час відновлення перевіряються всі посилання на збережені носії на відповідність розшифрованим хешам та записаним розмірам. Ідентичні перекриваючі сегменти журналу від перерваних завантажень дублюються; конфліктні перекриття відхиляються. Контрольні точки, створені під час завантаження, відкладаються доти, доки відповідні записи журналу не будуть скопійовані, тому завершений прохід не публікує контрольну точку раніше, ніж відповідний запис у журналі.

### Потокове експортування знімків екрана

Експорт у режимі потокової передачі використовує стабільний знімок для читання SQLite. Його початок може бути старішим за поточний активний журнал на момент завершення завантаження. Контрольна точка, створена після цього знімка, може правильно вказувати, що архів є коротшим; порівнюйте з контрольною точкою на рівні або перед експортованим префіксом, а не з довільною пізнішою контрольною точкою.

## Підготовка RFC 3161 та перевірка відповідей (0.2)

У режимі «Докази» (за замовчуванням вимкнений) надсилається лише SHA-256 кожного підписаного контрольного пункту до перелічених авторитетів RFC 3161 та календарів OpenTimestamps через маршрут робочого простору, але ніколи через MTProxy. Відповіді зберігаються у форматі `checkpoints/anchors/`, експортуються та перевіряються за допомогою `verify.py` (`--tsa-ca` перевіряє підпис TSA; підтверджуйте докази OpenTimestamps за допомогою `ots upgrade` та `ots verify`).

Панель готує архів запитів, що містить канонічні байти файлу checkpoint.json, відбиток повідомлення за алгоритмом SHA-256 та випадковий нонс у файлі timestamp.tsq, контрольні суми та інструкції. Мережеві запити не надсилаються. Підготовлений архів не є надійним часовим штампом.

`python -m tgwitness.anchor verify` двічі викликає OpenSSL: один раз для перевірки байтів контрольної точки та один раз для перевірки оригінального запиту. Потрібен явно вказаний файл довіреного ЦС; включені сертифікати автоматично не вважаються надійними. Перевірка запиту є необов’язковою в командному рядку, тому слід зберегти та вказати параметр --request для перевірки нонсу. Результат вказує, чи була проведена перевірка. Підпис контрольної точки колектора та журнал все одно потрібно перевіряти окремо за допомогою надійного відбитка колектора.

Поточний верифікатор не отримує інформацію про скасування сертифікатів та не здійснює довгострокову архівну перевірку після закінчення терміну дії сертифіката. Не слід ототожнювати успішну перевірку підпису з кваліфікованою часовою міткою або юридичною допустимістю. Під час розробки не було встановлено зв’язку з жодною справжньою незалежною організацією TSA. Див. [процедуру](https://phishdestroy.github.io/TgWitness/uk/docs/reference/timestamp/).
