前方秘匿性(PFS)と Signal のダブルラチェットの原理
鍵が一度漏れても過去のメッセージは守られ、未来の安全も自動で回復する。Signal のダブルラチェットがどう前方秘匿性と post-compromise security を両立させるかを原理から押さえられます。
- 1.前方秘匿性(PFS)は「いま鍵が漏れても過去は守る」性質。各メッセージ鍵を使い終わったら破棄し、KDF の一方向性で古い鍵を遡って復元できないようにする。
- 2.post-compromise security(PCS)はその逆で「過去に漏れても未来は回復する」性質。新しい DH 交換を定期的に注入し、攻撃者が知らない乱数で鍵チェーンをリセットする。
- 3.Signal は X3DH で最初の共有秘密を作り、対称ラチェット(PFS 担当)と DH ラチェット(PCS 担当)を組み合わせたダブルラチェットで両性質を同時に満たす。
解きたい問題:鍵は「いつか必ず漏れる」前提で守る
暗号の現実的な脅威モデルでは、鍵は永久に秘密でいられません。端末の押収、マルウェア、メモリダンプ――いつか鍵が攻撃者の手に渡る。問題は「漏れたら終わり」にしないことです。ここで二つの異なる性質が要ります。
- 前方秘匿性(PFS, Perfect Forward Secrecy):いま鍵が漏れても、それ以前に送った過去メッセージは復号できない。
- post-compromise security(PCS, 将来回復性):いま鍵が漏れても、その後いずれメッセージの安全が自動で回復する。
ECDHE が TLS にもたらすのは PFS だけです。長時間続く一本のセッションでは「途中で鍵が漏れたら、それ以降ずっと丸見え」という弱点が残ります。チャットのように何日も続く会話では、PFS に加えて PCS が欲しい。Signal のダブルラチェットは、この二つをメッセージ単位で達成する仕組みです。
PFS の原理:一方向 KDF で鍵を「捨てて、戻れなくする」
PFS の核心は単純です。各メッセージを別々の鍵で暗号化し、使い終わった鍵を即座に破棄する。そして古い鍵を新しい鍵から逆算できなくする。 これを支えるのが鍵導出関数(KDF)の一方向性です。
chain_key_0 ─KDF─▶ (chain_key_1, message_key_0)
chain_key_1 ─KDF─▶ (chain_key_2, message_key_1)
chain_key_2 ─KDF─▶ (chain_key_3, message_key_2)
...
各 message_key で1通だけ暗号化し、使ったら破棄する
これが**対称ラチェット(symmetric ratchet)**です。チェーン鍵を KDF に通して「次のチェーン鍵」と「今回のメッセージ鍵」を取り出し、自分は次のチェーン鍵だけを保持します。KDF はハッシュ系の一方向関数なので、chain_key_2 を奪っても chain_key_1 や過去の message_key_0 は復元できません。歯車(ratchet)が前にしか回らないのが名前の由来です。
各 message_key は1メッセージの暗号化(実体は AEAD)に使い、直後に破棄します。受信側も復号したらその鍵を捨てる。だから端末が後で奪われても、過去メッセージの鍵はもうメモリ上に存在せず、チェーン鍵から遡って作り直すこともできません。
ただし対称ラチェットだけでは PCS は得られません。攻撃者がチェーン鍵そのものを奪えば、そこから先のメッセージ鍵は全部 KDF で前送りに計算できてしまう。未来が守れないのです。ここで二つ目のラチェットが要ります。
PCS の原理:DH を注入してチェーンをリセットする
未来を回復する唯一の方法は、攻撃者が知らない新しい乱数を鍵チェーンに混ぜることです。それを供給するのが Diffie-Hellman です。
DH ラチェットでは、両者がエフェメラルな DH 鍵ペアを持ち、メッセージのやり取りに自分の新しい DH 公開鍵を相乗りさせます。相手の新しい公開鍵を受け取るたびに、自分の秘密鍵との間で新しい DH 共有秘密を計算し、それをルート鍵に混ぜてチェーンを丸ごと作り直します。
受信した相手の新 DH 公開鍵 + 自分の DH 秘密鍵
│
DH 計算 → 新しい共有秘密
│
root_key ─KDF─▶ (新 root_key, 新 chain_key) ← チェーンがリセットされる
肝心なのは、攻撃者がいま全状態(チェーン鍵もルート鍵も)を奪っても、次の DH ラチェットで両者が生成する DH 秘密鍵は新しい乱数だという点です。攻撃者はその秘密鍵を知らないため、新しい共有秘密を計算できず、リセット後のルート鍵を追えなくなる。一度の DH ステップを「両者が無事に通過」すれば、そこで攻撃者は締め出され、安全が回復します。これが PCS の正体です。
PCS は無条件ではありません。回復には、攻撃者に邪魔されず両者が新しい DH 鍵を交換できる必要があります。攻撃者が常時メッセージを改ざん・遮断できる能動的中間者であり続ける限り、回復は妨げられます。PCS が守るのは「受動的に状態を一度盗み見たが、その後は手を引いた攻撃者」に対する自己修復だと理解してください。
ダブルラチェット:二つを噛み合わせる
ダブルラチェットは、この対称ラチェットと DH ラチェットを階層的に組み合わせたものです。役割分担が明快です。
| 観点 | 対称ラチェット | DH ラチェット |
|---|---|---|
| 回す単位 | メッセージ1通ごと | 通信の向きが切り替わるごと |
| 供給する乱数 | なし(KDF の前送りのみ) | 新しい DH 共有秘密 |
| 主に担う性質 | 前方秘匿性(PFS) | 将来回復性(PCS) |
| 守る対象 | 過去のメッセージ鍵 | 将来のチェーン全体 |
| 入力 | 現在のチェーン鍵 | 相手の新 DH 公開鍵 + ルート鍵 |
実際の流れはこうです。送信チェーン・受信チェーンの中では対称ラチェットがメッセージごとに回り、PFS を細かく刻む。会話の向きが相手→自分に切り替わると、相手の新しい DH 公開鍵が届くので DH ラチェットが回り、ルート鍵と両チェーンが作り直されて PCS を回復する。両者は交互に DH 鍵を更新し合うので、会話が続く限り定期的に「攻撃者を締め出す機会」が訪れます。
ルート鍵チェーン(DH ラチェットが進める)
root_key ─DH─▶ root_key' ─DH─▶ root_key'' ...
│ │
送信/受信 送信/受信
チェーン鍵 チェーン鍵
│ │
対称ラチェット 対称ラチェット ← メッセージ鍵を1通ずつ生成
ネットワークではメッセージが前後したり落ちたりします。ダブルラチェットは、まだ来ていない番号のメッセージ鍵を先に計算してスキップ鍵として保管できます。後から古い番号が届いても、保管した鍵で復号可能。これがあるため、非同期で順不同なモバイル通信でもラチェットが破綻しません。
最初の鍵はどう作る:X3DH
ラチェットを回すには、最初の共有秘密(初期ルート鍵)が必要です。ところが相手はオフラインかもしれない。これを非同期で解くのが **X3DH(Extended Triple Diffie-Hellman)**です。
各ユーザーはあらかじめサーバーに鍵バンドルを預けます。長期の身元鍵(IK)、中期の署名付き事前鍵(SPK)、使い捨ての一回限り事前鍵(OPK)です。送信者は相手のバンドルを取得し、自分の身元鍵とエフェメラル鍵を使って複数の DH を実行し、その結果を連結して KDF に通すことで初期共有秘密を導きます。
DH1 = DH(送信者IK, 相手SPK) ← 送信者の身元を共有秘密に結ぶ
DH2 = DH(送信者EK, 相手IK) ← 相手の身元を共有秘密に結ぶ
DH3 = DH(送信者EK, 相手SPK) ← 前方秘匿性に寄与
DH4 = DH(送信者EK, 相手OPK) ← 使い捨て鍵で各セッションを独立化
初期共有秘密 = KDF(DH1 ‖ DH2 ‖ DH3 ‖ DH4)
→ この値をダブルラチェットの最初の root_key にする
なぜ DH を複数組むのか。それぞれの身元鍵を絡めた DH(DH1 が送信者の IK を、DH2 が相手の IK を共有秘密に織り込む)が相互認証を担い、なりすましを防ぎます。一方、エフェメラル鍵と一回限り事前鍵を絡めた DH(DH3/DH4)がPFS とセッションの独立性を与えます。SPK は身元鍵で署名されているため、電子署名による相手認証も成立します。こうして相手がオフラインでも、認証付きで前方秘匿な初期鍵を一発で確立できるのです。
ダブルラチェットも X3DH も、相手の身元鍵が本物だと信じられて初めて成立します。身元鍵の検証(Signal の「安全番号」照合)を怠ると、能動的中間者が最初から両者になりすませてしまい、PFS も PCS も攻撃者の手の中で完結します。鍵共有・前方秘匿性・相手認証は別問題だという原則はここでも効きます。
まとめ
前方秘匿性は KDF の一方向性で過去の鍵を不可逆に捨てることで、post-compromise security は 新しい DH の乱数を注入してチェーンをリセットすることで達成されます。Signal のダブルラチェットは、メッセージ単位で前者を刻む対称ラチェットと、向きの切り替わりで後者を回復する DH ラチェットを噛み合わせ、両性質をメッセージ粒度で同時に満たします。最初の共有秘密は X3DH が非同期かつ認証付きで確立し、以降のすべての鍵がそこから派生します。
土台となる鍵共有とエフェメラル鍵の考え方は Diffie-Hellman、メッセージ本体を守る暗号は AEAD、鍵チェーンを支える一方向関数は ハッシュ関数の内部構造 と併せて押さえると、なぜこの設計で「漏れても守れる」のかが原理から腑に落ちます。
セキュリティ Article
前方秘匿性(PFS)と Signal のダブルラチェットの原理を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
前方秘匿性
比較で見る軸
難易度: advanced / カテゴリ: セキュリティ / タグ数: 5
導入後に効く点
post-compromise security(PCS)はその逆で「過去に漏れても未来は回復する」性質。新しい DH 交換を定期的に注入し、攻撃者が知らない乱数で鍵チェーンをリセットする。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- セキュリティ
- タグ数
- 5
判断チェックリスト
- 自社の用途が「前方秘匿性 / Signal」に近いか確認する。
- 強みである「前方秘匿性(PFS)は「いま鍵が漏れても過去は守る」性質。各メッセージ鍵を使い終わったら破棄し、KDF の一方向性で古い鍵を遡って復元できないようにする。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。