セキュリティモデル
ランタイムゲートが実際に何を検証しているか、デバイスロックのエラーがなぜ意図的にあのような形になっているか、そして各レベルの正直な限界について。
保護とは、あるかないかだけの単純なスイッチではありません。保護対象の各モジュールに挿入され、そのモジュールがインポートされるたびに、デバイス固有の暗号材料を使って完全にオフラインで走る、ランタイムの検証です。
ゲートが検証すること
ゲートされたコードが実行されるたびに、MagicLock はローカルで、以下の順序で検証を行います。
- ライセンスが本物であること — あなたのアカウントから発行されたものであり、その署名が検証できること。
- ライセンスがまだ有効であること — 期限切れでも失効済みでもないこと。
- デバイスが一致すること — ライセンス(または成果物)が固定されている、まさにそのマシンであること。
- ケーパビリティが許可されていること — これから実行される内容が、ライセンスの許可する範囲内にあること。
- 証明が新しいこと — 一度限りの証明情報により、キャプチャ済みの古い検証結果を再生する攻撃を排除すること。
いずれかのステップが失敗すると、保護対象のコードが実行される前に処理は停止します — 部分的な実行や「ベストエフォート」的な経路は存在しません。
マシン違いとファイル改ざんが同じに見える理由
VaultLockedError(API リファレンスを参照)は、成果物が誤ったマシンで開かれた場合と、そのローカル状態が改ざんされた場合の両方で送出されます。これは意図的な設計です。両者を区別してしまうと、検証がなぜ失敗したのかという情報を漏らすことになり、それはまさに、弱点を探る攻撃者が欲しがる類のオラクルです。どちらの場合でも、エラーメッセージは同じままです。
改ざんを検知するだけでなく、証明する
暗号化された成果物は、暗号化されているだけでなく認証もされています — 1 バイトでも書き換えられていれば、復号が始まる前に認証で失敗します。改変されたファイルを部分的に復号して中身を確認する、といったことはできません。単に実行を拒否するだけです。
各レベルが実際に防ぐもの
自分がどのようなトレードオフを選んでいるのか、正確に理解しておいてください。
- 簡易版(
.pya) — 単純なコピーや、気軽なソースコード閲覧を防ぎます。アプリの実行中、復号されたバイトコードは必然的にプロセスのメモリ上に存在します。デバッガを使い、実行中のプロセスを完全に制御できる、十分な動機を持つ攻撃者であれば、原理的にはそれを取り出すことができます。このレベルが目指すのは「容易にはできないようにする」ことであり、メモリフォレンジックを打ち破ることではありません。 - コンパイル版(
magiclock build) — 平文の Python は一切出荷されず、ライセンス確認はビルド時にコンパイル後のすべてのモジュールの制御フローへ自動で織り込まれ、1 箇所の削除可能な共有チェックに集約されることはありません。これにより、最も単純なバイパス(「チェックを消すだけ」)が塞がれ、必要なハードルは「ソースを読む」ことから「コンパイル済みバイナリをモジュールごとにリバースエンジニアリングする」ことへと引き上げられます。 - デバイス固定 — 復号を、ローカルの端末/OS 識別子から導出したマシンフィンガープリントに紐づけます。マシン間での気軽なコピーには耐えますが、物理的なセキュアエレメントの代替にはなりません。十分なリソースを持ち、デバイスを完全に制御できる攻撃者であれば、そのフィンガープリントの偽装を試みる可能性があります。
MagicLock は、コピー・改ざん・リバースエンジニアリングのコストを意味のある形で引き上げるように設計されています — ソフトウェアのみの保護が、時間無制限で端末を完全に掌握した攻撃者に対して破られないと主張するものではありません。あなたの脅威モデルにそのレベルの攻撃者が含まれる場合、そのために存在するのが物理的な保護機構です。
署名と鍵
ライセンスは Ed25519 で署名され、ベンダーキーに対して検証されます — 同じ署名方式が失効リストの保護にも使われているため、古い、あるいは偽造された失効情報を再生して失効を取り消すことはできません。検証鍵は出荷物に埋め込まれますが、署名用の秘密鍵がライセンスサーバーの外に出ることは一切ありません。
関連ページ
- アカウントと有効化 — オフライン猶予枠、および失効が影響すること・しないこと。
- Python API リファレンス — 各失敗モードがコード内で送出する例外について。