# 証拠の形式

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

署名付きZIPファイルの内容、記録の種類、処理の受領確認、ユーザープロフィール、オフサイトコピー、およびオフライン検証について。

観察結果は結論ではありません。送信者IDは実世界の身元ではありません。 アカウント上で確認できるサービス更新情報が情報源であり、Telegramは第三者の調査員のために、 これらの保存記録に暗号学的署名を行っていません。

## 署名とは

| 資料 | 保護 | 保存すべき内容 |
| --- | --- | --- |
| 運用上のイベントを含む、ジャーナルへのすべての記録 | イベントごとにSHA-256チェーンとEd25519署名を付与します | オリジナルの`events.jsonl`（信頼できる鍵のフィンガープリント）および保持されたチェックポイント |
| 証拠ZIPファイルの内容 | `manifest.sig`は、コンテンツファイルのハッシュを一覧にした正規のマニフェストに署名します。 | マニフェストおよび署名を含む完全なZIPファイル |
| 保存された添付ファイル | 署名付き`media.saved`イベント内のSHA-256およびバイト数；署名付きZIPマニフェスト内のファイルハッシュ | 検証済みのZIPファイル内の元のバイトデータ |
| チェックポイント | ジャーナルの長さ、先頭ハッシュ、公開鍵、および作成時刻に対するEd25519署名 | 変更の是非が争われた際に備えて保持された独立したコピー |
| 実行記録 | 個別に署名されたコレクターの測定値。これらは、署名付き`process.started`イベントにも含まれています。 | 受信記録および関連するジャーナル。測定値は自己申告によるものです。 |
| スタンドアロンのCSVまたはHTML | 独立した暗号署名はありません。イベントのハッシュだけでは不十分です。 | 完全性チェックのためには、検証済みのZIPファイル内の正確なコピーをご使用ください。 |
| ブラウザによって生成されたスクリーンショット、印刷物、またはPDF | 派生作品にはTgWitnessの署名が含まれていません | 検証済みの元のパッケージを保管し、派生版がどのように作成されたかを文書化してください。 |

このコレクターは、あらゆるエクスポート形式を生成する前に、ジャーナルの署名を検証します。 このソースチェックでは、別途ダウンロードされたCSVや HTMLファイルに対して署名が作成されることはありません。同様に、有効なJSONLイベント署名であっても、添付ファイルの バイトデータを提供したり、観測されたジャーナル全体が提供されたことを独立して立証したりするものではありません。

## 調査の引き継ぎ

1. 案件番号、担当者、Telegramアカウントの数値ID、選択した 情報源、収集方針、および収集期間を、案件メモに記録してください。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、エクスポート境界、転送日時、送信者、および 受信者を保管記録に記録してください。元のパッケージはそのままの状態で保管し、 作業はコピーで行ってください。アカウントのセッションや署名用秘密鍵も含まれる アプリケーションのプライベートデータディレクトリではなく、証拠ファイルを共有してください。 転送の際は、エクスポートデータをパスワードで保護されたAES-256 ZIPファイルに格納することができます。 パスワードは機密性を保護するのみであり、保存されることはありません。受信者は アーカイブを解凍し、内部にある署名付きエクスポートデータが変更されていないことを検証します。
   2. 独立したタイムアンカーが必要な場合は、 [RFC 3161の手順](https://phishdestroy.github.io/TgWitness/ja/docs/reference/timestamp/)に従い、リクエスト、チェックポイント、信頼できる CA情報、および検証済みの認証局の応答を保持してください。リクエストを準備するだけでは、 タイムスタンプは取得されません。

検証が成功した場合、信頼できる収集者の 鍵および保持された境界に対する完全性が確立されます。ソースの真実性、Telegramの完全な配信、信頼できる時刻、 および法的な証拠能力については、別途証拠が必要となります。本バージョンは、 Telegramの実稼働環境での適格性評価や、独立したフォレンジック／セキュリティ評価をまだ完了しておりません。

コピーされたコレクターキーを使用すると、偽造された代替ジャーナルに署名することが可能です。独立して保持されたチェックポイントやウィットネスレコードとの照合により、 以前のプレフィックスが変更されたことが検出されますが、 署名の検証だけでは検出できません。 ウィットネスはコレクターが提示した内容を修正するものであるため、 最初の外部チェックポイント以前に捏造された記録の真実性を 独自に立証することはできません。クラウド展開レポートは、設定およびコレクターから報告された 測定値を確認するものであり、ブートストラップ中に秘密鍵がコピーされなかったことを 立証するものではありません。

Appriseを通じて送信される通知には、ワークスペース名、チャット名、および件数が含まれます。削除・編集されたメッセージの短い 抜粋は、オペレーターが機能を有効にした場合にのみ 追加されます（デフォルトでは無効です）。これらは利便性を高めるためのシグナルであり、証拠ではありません。配信は保証されず、 観察された内容はジャーナルおよびその署名付きエクスポートにのみ記録されます。 ワークスペース プロキシを使用する場合、通知は同じ経路を通ります。この経路を使用できないサービスや、MTProxy経由の経路は拒否されます。

## イベントの正規化

ハッシュの入力は、正確に以下のフィールドで構成されています：

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

UTF-8 形式の Python 互換 JSON を使用してシリアライズし、オブジェクトのキーはソート済み、ASCII エスケープなし、無意味な空白なし、NaN/無限大なしとします：

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

これらのバイトのSHA-256ハッシュがイベントハッシュとなります。署名は、 32バイトの生のハッシュ値に対してEd25519を適用し、その後Base64エンコードされたものです。最初のprevious_hashは64個のゼロで構成されます。 シーケンス番号は1から始まり、1ずつ増加します。運用上の イベントを含むすべてのイベントは、1つのチェーンを共有します。重複配信の抑制機能はありません。つまり、繰り返して 観測されたものは、個別の受信記録であり、個別のTelegramアクションの主張ではありません。

Telegramの生データの日付はISO文字列であり、バイナリフィールドは bytes_base64を含むオブジェクトとなります。メッセージ本文、エンティティ、メディア記述子、返信メタデータ、リアクション、 およびサービスアクションは、提供された場合、生メッセージのメタデータとして保持されます。

observed_at は、コレクターの UTC ウォールクロックです。source_at は、利用可能な場合に提供される Telegram の日付です。 削除通知には正確な削除時刻が含まれていないため、 source_at は null のままとなります。受信順序は、必ずしもソースイベントの 順序とは限りません。履歴のスナップショットでは、古いソース日付が使用されますが、観測時刻は現在の時刻となります。

## アクターのセマンティクス

message.new の actor_id はメッセージの送信者です。message.edited には推定された 編集者は含まれません。payload.sender_id には、観測されたメッセージの送信者が記録されます。 削除 actor_id は null のままとなります。メンバーシップサービスのメッセージは、送信者とアクション タイプを保持しますが、すべての削除を「自発的な退会」として扱ったりはしません。生の参加者 更新情報では、提供されたままの actor_id が保持されます。リアクションのスナップショットでは、変更を行ったアクターを架空のものとして生成することはありません。

プライベートチャットおよび通常のグループにおける削除通知には、メッセージIDのみが含まれます。 これらのチャットはアカウント全体で共通のIDシーケンスを共有しているため、1つのIDは このアカウントの単一のメッセージを識別します。 そのIDを持つメッセージが以前にこのジャーナルに保存されていた場合、 削除は当該チャット内で「message.deleted」として記録され、メソッド名を 「payload.chat_resolution」として指定されます。保存されたことのないIDは、「deletion.unscoped」として、 chat_idなしで保持されます。推測による処理は一切行われません。 チャンネルやスーパーグループでは、チャットごとにメッセージが番号付けされており、 この方法では照合されることはありません。削除されたメッセージが監視対象のアカウントによって送信された場合、 payload.deleted_message_was_outgoing は true となりますが、削除者は依然として指定されません。

## 対象範囲：連続性、欠落、および検出されなかったID

署名付きZIPファイルにはすべて`coverage.json`が含まれており、HTMLレポートは同じ要約で始まります。また、 `verify.py`はファイルをそのまま信頼するのではなく、`events.jsonl`から要約を再計算します：

- **XからYまで継続的に監視されます。**1つの期間は`observation.started`で始まり、 一時停止、停止、Telegram接続の切断、ログアウト、正常な停止を伴わないプロセスの起動、 または監視中に3分以上記録がない場合に終了します （ハートビートは1分ごとに記録されるため、記録がない状態は、プロセスが停止した、フリーズした、または コンピュータがスリープ状態になったことを意味します）。**「パラメータ**」でハートビート間隔が変更された場合、各ハートビートが その変更を記録し、許容される無記録時間は3つの間隔分となり、3分を下回ることはありません。
- **「ギャップ」**には、各期間間の時間と理由が記載されています。最後の 記録時点で未解決のギャップは、`not_resumed_by_last_record` とマークされます。
- **メッセージIDは観測されませんでした。**チャットごとにメッセージ数をカウントするチャンネルやスーパーグループについては、 観測されたIDの間に欠落しているIDは範囲として記載されています。これらは 「*観測されなかった*（削除された、配信されなかった、または保存された履歴の範囲外）」として報告され、決して 「削除された」とは記載されません。 プライベートチャットや通常のグループはアカウント全体で共通の連番を使用しているため、そこでの 欠落は意味を持たず、一覧には記載されません。
- チャット単位のデータ欠落通知（例えば、Telegramが特定の チャンネルにおいてデータ差分の復元を要求する場合など）は「メモ」としてカウントされ、全体的な記録範囲を中断することはありません。

「カバー範囲」では、このコレクターが何を記録したかを説明しています。これは、Telegramが 連続した期間中にすべての更新を配信したことを証明するものではありません。

## プロデューサー

証拠用ZIPファイル、チャットエクスポート、ウィットネス・ジャーナル、および人物プロファイルファイルの 署名付きマニフェストには、`producer` （ソフトウェア、バージョン、プロジェクト（PhishDestroy）、 ホームページ、ソースリポジトリ）が含まれています。HTMLレポートにも同様の行が表示され、`verify.py` は検証結果の一部としてこれを表示します。 このフィールドが存在する以前に作成されたアーカイブは、 従来通り検証され、作成者が特定されていないものとして報告されます。

## 人物プロフィール

プロファイルファイル（`tgwitness-dossier-v1`、JSON）は、リクエストに応じて、 1つのチャットに関するジャーナルにすでに記録されているデータに基づいて生成されます。Telegramからデータを取得することはなく、プロファイルの内容が ジャーナルに書き込まれることもありません（プロファイルのオン／オフの切り替えは`dossier.changed`として記録されます）。 メッセージが保存されているすべてのユーザーについて、名前とユーザー名の履歴、保存時点でのTelegramのフラグ （ボット、詐欺、偽アカウント、プレミアム、認証済み、制限付き、削除済み）、プロフィール 写真ID、ローカルのアバターキャッシュ内の類似アバターからのアバターの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
```

署名を検証し、証拠エクスポート自体（署名、ハッシュチェーン、ファイル）を検証するとともに、 参照されているすべてのレコードが、同じワークスペースのそのエクスポート内に変更されずに存在することを確認します。アバターのハッシュは、ローカルキャッシュに由来するものとしてラベル付けされます （ジャーナルレコードではありません）。メンションはパターン一致であり、確認すべき手がかりであり、検証済みの事実ではありません。 プロファイルはチャットごとに有効化され、パスワード付きのAES-256 ZIPとしてエクスポート可能で、 無効化された場合、またはチャットがソースから削除された場合に削除されます。

## 独立した証人

ウィットネス・ジャーナル（`tgwitness-witness-v1`）およびそのチャンネルへの投稿は、 `verify.py --witness … --channel …` による証拠エクスポートと照合されます。[独立したウィットネスについては](https://phishdestroy.github.io/TgWitness/ja/docs/reference/witness/)、こちらをご覧ください。

## マニフェストとチェックポイント

manifest.json には、フォーマット、エクスポート日時、公開鍵、フィンガープリント、ジャーナルの 長さ、先頭ハッシュ、スコープ、プロデューサー、制限事項、および `manifest.json` と `manifest.sig` 自体を除く すべてのコンテンツメンバーの SHA-256 ハッシュが含まれています。 manifest.sig は、正規化されたマニフェスト JSON に対する Base64 エンコードされた Ed25519 署名です。 検証ツールは、予期しないメンバーや欠落したメンバー、重複する名前、破損したファイル、 シーケンスやチェーンの破損、無効な署名、および提供されたチェックポイントとの不一致を拒否します。 アーカイブのパスを抽出することはありません。宣言された総非圧縮サイズは 4 GiB に制限されています。 「`--max-gib N`」では、**Parameters** の制限値を引き上げることで、より大きなアーカイブを作成することが可能です。

検証コードは、信頼できるチェックアウトまたは審査済みの独立したコピーから入手してください。信頼できないアーカイブ内の検証プログラムは、 そのアーカイブの作成者によって提供された実行可能ファイルです。 その存在は移植性の利便性を目的としたものであり、独立した信頼の根拠ではありません。

チェックポイントは、公開鍵、ジャーナルの長さ、先頭 ハッシュ、および作成時刻を含むステートメントに署名します。検証者は、後のアーカイブに 同じプレフィックスが含まれていることを確認できます。係争の恐れのある変更が行われる前に、コレクターの外側にチェックポイントを保持しておいてください。 アーカイブとチェックポイントの両方を制御している攻撃者は、両方を置き換えてしまう可能性があります。

ピン留めされていない鍵による有効な署名は、内部の一貫性を確立するに過ぎません。 たとえピン留めされた鍵であっても、記録前に収集者または鍵が 侵害されていた場合、ソースコンテンツの真実性を証明することはできません。外部によるタイムスタンプの付与と信頼できるキャプチャは、 別個の要件となります。TgWitnessは、証拠としての採用可能性や法科学的な認証を保証するものではありません。

## 添付ファイル

ファイルは media/ ディレクトリ内で SHA-256 ハッシュによって識別されます。media.saved イベントは、ハッシュと バイト長を特定のメッセージイベントに関連付けます。生のファイル名はメタデータであり、決してローカル パスではありません。 メディアのバイトデータは非同期でダウンロードされるため、即時削除を行うと キャプチャできない場合があります。収集が無効化されている場合、サイズ超過またはサイズ不明のファイル、ダウンロードの失敗、 およびダウンロードの中断は、結果として表示されます。プロセスのクラッシュにより、 参照されていない部分的なファイルが残る場合がありますが、これらはエクスポートには含まれません。

ZIPエクスポート機能は、保存されたメディアをそのファイル名と照合して検証します。JSONL、CSV、およびHTML 形式でのエクスポートは、イベントの署名を検証しますが、メディアのバイナリデータは含まれません。派生レポートは 利便性を目的としたものであり、events.jsonlがイベントの正式な表現となります。

## 永続的な収集と受領証の処理 (0.2)

メディアジョブとそのメッセージイベントのコミットは、1つのトランザクションで行われます。 ジョブは 元のTLメッセージ記述子を保存します。署名付き観測データには、そのSHA-256、アカウント、 およびサイズポリシーが紐付けられます。復元時には、ダウンロード前にその紐付けが検証されます。現在のメッセージを取得することは決してなく、 新しい添付ファイルに静かに置き換えられます。中断されたジョブは 再試行されますが、恒久的な失敗は明示的な結果として残ります。

各ワークスペースには、独自のジャーナルと署名用IDが割り当てられています。「process.started」イベントには、 実行ID、ローカル時刻、依存関係のバージョン、ソースファイルのハッシュ、初期アカウント／スコープ、 およびキャプチャ設定が含まれます。ハートビートには、実行ID、単調時間、およびキューの状態が含まれます。 プロセス領収書は、個別に署名されたステートメントとしてダウンロードすることも可能です。 ファイルの測定値は自己申告によるものであり、信頼できるハードウェアによる証明ではありません。

自動チェックポイントは、起動時、監視中（**[パラメータ] → [署名付きチェックポイントの生成間隔**]）に5分ごとに、 および正常な停止時にローカルに保持されます。 起動時の検証およびエクスポートでは、ジャーナルを ローカルに保持されている最新のチェックポイントと照合します。これにより、チェックポイントが残存している場合にデータベースのロールバックを 検出できます。ただし、両方を管理しているオペレーターであれば、両方を上書きすることも可能です。 独立した保存期間の設定と、外部の信頼できるタイムスタンプは、引き続き別個の要件となります。

再起動後、保存されていたTelegramの更新位置（`catch_up_from`から `observation.started`）が再開され、見逃された更新が要求されます。定期的な再確認により、見逃されたメッセージについては `message.recovered`が記録され、Telegramが保存されたメッセージを 返さなくなった場合は`message.missing_on_recheck`が記録されます。後者は削除通知ではなく、削除者や時刻は記載されません。

### オフサイトでのコピーおよび復元（S3）

オフサイトコピー（エクスポート、デフォルトでは無効）では、ジャーナルのセグメント、保存されたファイル、チェックポイント およびそれらのタイムスタンプ、完了したチャットのエクスポートデータを、S3互換のストレージにアップロードします。これらは AES-256-GCMで暗号化された上でアップロードされます。バケットが対応している場合は、オブジェクトロックの保存期間が適用されます。 ダウンロードした鍵がなければ、コピーを読み取ることはできません。復元および検証は `python -m tgwitness.replica restore --key-file KEY.json --out DIR`で行ってください。

復元には、空の通常のディレクトリが必要です。ダウンロードされたデータおよび復号化されたオブジェクトは、 ジャーナル全体および保持されているすべてのチェックポイントが検証されるまで、非公開の一時ストレージに保管されます。安全でないオブジェクト パス、不整合なセグメント範囲、およびチェックポイントよりも短いジャーナルは拒否されます。検証に失敗した場合、 復元されたファイルは公開されません。 保持チェックでは、返されたオブジェクトロック モードと日付を、アップロード時に送信された期限と比較します（丸め誤差は1秒）。期限切れ、 無効、または短縮された期限は、検証済みとはみなされません。ステータスには、そのアップロードパスにおいて保持情報が読み取られた サンプリングされたオブジェクトが報告されます。

コピーの進行状況は、保存先および暗号化キーの範囲に限定されます。エンドポイント、バケット、 またはプレフィックスを変更すると、ローカルに保持されているジャーナル、メディア、およびチェックポイントの 完全なコピーが、その保存先へ開始されます。範囲外だった過去の進行状況は保持されますが、アップグレード後に一度再実行されます。ローカルの送信トレイからすでに削除された過去の チャットエクスポートは、以前の保存先に残ります。

ローカルメディアの欠落や破損は、そのパスのエラーとして報告され、メディアカーソルは その位置に留まるため、次のパスでファイルの再試行が行われます。その後のファイル、チェックポイント、およびエクスポートは 引き続きコピーされます。復元時には、 保存されたすべてのメディア参照について、復号化されたハッシュおよび記録されたサイズとの照合が行われます。 中断されたアップロードによる同一の重複する ジャーナルセグメントは重複排除され、競合する重複部分は拒否されます。 アップロード中に作成されたチェックポイントは、それに対応するジャーナル記録が コピーされるまで保留されるため、完了したパスでは、ジャーナルより先にチェックポイントが公開されることはありません。

### ストリーミングによるスナップショットのエクスポート

ストリーミング形式でのエクスポートでは、安定したSQLiteの読み取りスナップショットが使用されます。ダウンロードが完了した時点で、その先頭部分は 現在のライブジャーナルよりも古い場合があります。そのスナップショット後に作成された チェックポイントでは、アーカイブのサイズが実際より小さいと正しく報告されることがあります。比較する際は、エクスポートされたプレフィックス時点またはそれ以前のチェックポイントと 比較し、恣意的に後日のチェックポイントと比較しないようにしてください。

## RFC 3161 に基づく準備および応答の検証 (0.2)

エビデンスモード（デフォルトではオフ）では、各署名付きチェックポイントのSHA-256のみを、リストされた RFC 3161認証機関およびOpenTimestampsカレンダーへ、ワークスペース経由で送信し、MTProxyを経由することは一切ありません。 応答は`checkpoints/anchors/`に保存され、エクスポートされた後、`verify.py`によって検証されます（`--tsa-ca`は TSA署名を検証し、`ots upgrade`および`ots verify`でOpenTimestampsの証明を確認します）。

このパネルは、正規化された checkpoint.json バイトデータ、 SHA-256 メッセージインプリント、および timestamp.tsq 内のランダムなノンス、チェックサム、ならびに手順書を含むリクエストアーカイブを作成します。 ネットワークリクエストは行われません。この準備作業は、信頼できるタイムスタンプではありません。

`python -m tgwitness.anchor verify`はOpenSSLのts関数を2回呼び出します。1回目はチェックポイント バイトに対して、2回目は元のリクエストに対してです。信頼できるCAファイルが明示的に必要となります。 同梱の証明書は自動的に信頼されるわけではありません。CLI ではリクエストの検証は オプションですので、ノンスを検証するには `--request` オプションを指定してください。結果には、 検証が行われたかどうかが表示されます。コレクターのチェックポイント署名およびジャーナルは、 信頼できるコレクターのフィンガープリントを用いて別途検証する必要があります。

現在の検証ツールは、失効情報を取得せず、また証明書の有効期限切れ後の長期 アーカイブ検証も実装しておりません。署名の検証に成功したからといって、 認定タイムスタンプや法的証拠能力があるものと見なさないでください。開発期間中、 真に独立したTSAには連絡を行っておりません。[手順](https://phishdestroy.github.io/TgWitness/ja/docs/reference/timestamp/)をご参照ください。
