コンテナ形式
このページでは、固定かつ公開された中程度の強度のベースラインコンテナ形式(v1)について説明します。本番の配布用ビルドはクローズドの多態化エンジンを経由し、このエンジンがビルドごとにレイアウトと鍵導出をランダム化します。本仕様が記述するのは、オープンなフレームワークが実装し、テストしている固定のベースラインです。
整数はすべてリトルエンディアンです。暗号化にはすべて AES-256-GCM(16 バイトの認証タグ)を使用します。
レイアウト
ヘッダー(64 バイト)
ヘッダーの先頭 60 バイト(reserved 以外のすべてのフィールド)はインデックスの GCM AAD となるため、ヘッダーを改変するとインデックスを復号できなくなります。
インデックス
インデックスは GCM で暗号化された UTF-8 の JSON オブジェクトです。
name:Ren'Py のゲームディレクトリからの相対パス。スラッシュ区切りで、先頭のスラッシュは付かない(例:images/bg.png)data_offset:そのファイルの最初の暗号化チャンクの絶対オフセットsize:平文のサイズ(バイト単位)file_id:16 バイトの乱数(16 進表記)。ファイルごとに一意で、ファイル鍵のソルトとなる
ファイルペイロード
各ファイルの平文は chunk_size バイトごとのチャンクに分割されます(空のファイルは 1 つの空のチャンクとして扱われるため、この場合も認証タグを持ちます)。各チャンクは個別に暗号化され、ciphertext || tag(平文の長さ + 16)の形で格納されます。最後のチャンク以外はすべて平文をちょうど chunk_size バイト保持するため、ファイルのチャンク i は常に data_offset + i*(chunk_size + 16) から始まり、走査を行わずにシークによるランダムアクセスが可能です。
鍵導出(HKDF-SHA256、RFC 5869)
master_keyは 32 バイトで、実行時にこれをどこから得るかは本ベースライン仕様の範囲外である。オープンなフレームワークはこれを入力として受け取り、配布されるゲームではクローズドの鍵導出ロジックがこれを隠蔽し再構築する- ファイルごとの鍵により、ノンスの方式の適用範囲はファイルごとに 1 つの鍵に限定される
チャンクごとのノンス(12 バイト)と AAD
カウンターによって各ノンスはファイル内で一意となり、ファイルごとに一意な鍵によってファイル間でも一意となります。AAD は各チャンクをその位置に結び付け、最後のチャンクに印を付けるため、チャンクの並べ替えはできず、ファイルを気付かれないように切り詰めることもできません。
セキュリティに関する注記(ベースライン)
- GCM によりチャンクごとに機密性と完全性を確保するため、どこが改ざんされても復号は失敗する
- この形式は公開かつ固定であるため、単独ではベースラインの強度しか持たない。汎用的な読み取りツールが成立するのは、攻撃者がマスター鍵も保持している場合に限られる。実際の障壁は実行時の鍵導出(クローズド)とビルドごとの多態化(クローズド)であり、いずれも本仕様の対象外
- 設計上の対象外:メモリからの抽出と画面のキャプチャ(脅威モデルを参照)
リファレンス実装
- 書き込み:
src/renmori/container/writer.py - 読み込みとストリーミング:
src/renmori/container/reader.py - AES-GCM バックエンド:
src/renmori/container/crypto.py - 形式の定数と鍵導出:
src/renmori/container/format.py