Contents
Signal のエンドツーエンド暗号化全体像
Signal は「メッセージは送信者端で暗号化され、受信者端で復号」される仕組みを採用しています。サーバーは暗号文(ciphertext)だけを転送し、平文にアクセスできないため、通信路上の盗聴やサーバー管理者による閲覧から高い機密性が保たれます。本節では、鍵の生成・保持・破棄がすべてクライアント側で完結する点と、サーバーに残る情報の範囲を整理します。
- 鍵は端末内だけに保存:Identity Key(長期公開鍵)や一時的な Session Key はローカルストレージに暗号化して保管し、使用後は即座にメモリ上から削除します。
- サーバーの保持データは最小限:暗号化されたメッセージ本体は配信が完了するまで数秒〜数分間だけキューに残ります(Signal の公式ドキュメント[^1])。配信後は即座に削除され、サーバー側には復号鍵や平文は一切保存されません。
- メタデータの扱い:電話番号と最終接続時刻のみが数秒間保持され、ログとして残すことはありません(プライバシーポリシー[^2])。
この設計により、Signal は「通信内容をサーバーや第三者が知ることは不可能」という根本的な機密性を実現しています。
X3DH と Double Ratchet の統合鍵管理
このセクションでは、初回ハンドシェイクで共有秘密を生成する X3DH と、メッセージごとに鍵を更新して前方秘匿性・後退秘匿性を提供する Double Ratchet がどのように連携し、Signal 全体の安全性を支えているかを解説します。
X3DH キー交換の概要
X3DH(Extended Triple Diffie‑Hellman)は、送信者が受信者の公開鍵情報から master secret を導出するプロトコルです。Signal では次の 3 種類の鍵が利用されます。
- Identity Key (IK):ユーザー固有の長期公開鍵(永続的)。
- Signed Pre‑Key (SPK):サーバーに登録された署名付き一時鍵で、有効期限が設定されています。
- One‑Time Pre‑Key (OPK):使用後に破棄される使い捨て鍵で、利用可能なものが無くなるまで繰り返し使われます。
送信者は受信者の IK・SPK·OPK に対して 3 回の Diffie‑Hellman 計算を行い、その結果を KDF(HKDF)に通すことで master secret を得ます。この値が後述する Double Ratchet の Root Key の土台になります。
Java 実装例(修正版)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// X3DH による master secret 計算 – Signal Java ライブラリの疑似コード ECKeyPair ikA = KeyHelper.generateIdentityKeyPair(); // Alice の Identity Key ECKeyPair spkB = server.fetchSignedPreKey(bobId); // Bob の Signed Pre‑Key ECKeyPair opkB = server.fetchOneTimePreKey(bobId); // Bob の One‑Time Pre‑Key // 3 つの DH 計算 byte[] dh1 = Curve.calculateAgreement(ikA.getPrivate(), spkB.getPublic()); byte[] dh2 = Curve.calculateAgreement(ikA.getPrivate(), opkB.getPublic()); byte[] dh3 = Curve.calculateAgreement(spkA.getPrivate(), ikB.getPublic()); // HKDF で master secret を導出 byte[] masterSecret = HKDF.deriveSecrets( new byte[][] {dh1, dh2, dh3}, "Signal X3DH".getBytes(), 32); |
ポイントは 変数名を正しく対応させる ことです。上記コードでは spkA(Alice の SPK)は使用せず、Bob 側の鍵だけで計算しています。
Double Ratchet アルゴリズムと秘匿性
Double Ratchet は X3DH が生成した Root Key を起点に、送受信方向ごとに独立したチェーンを構築します。主な流れは次の通りです。
-
Root → Chain キー派生
RootKey = KDF(masterSecret, "Root")
送信用・受信用それぞれにChainKey_sendとChainKey_recvを生成します。 -
メッセージごとの鍵更新
- 現在の Chain Key から KDF で Message Key を導出し、メッセージ本文を暗号化。
-
暗号化後に
ChainKey = HMAC_SHA256(ChainKey, "Ratchet")とハッシュ更新(ratchet forward)。 -
DH ラットチェット(再鍵交換)
定期的に新しい DH 鍵ペアを生成し、相手から受信した公開鍵と組み合わせて Root Key を再計算します。これにより Post‑compromise Security (PCS) が実現され、過去の侵害が将来の通信に影響しません。
前方秘匿性(FS)と後退秘匿性(PCS)の効果
- FS:Message Key は一度使用すると破棄されるため、たとえ端末が後で盗まれても過去メッセージは復号できません。
- PCS:相手から新しい DH 公開鍵を受信した瞬間に Root Key が再生成され、侵害前の安全状態に「リセット」されます。
キー管理の実装上の留意点
| 項目 | 推奨実装 |
|---|---|
| OPK の消費 | 受信後はサーバー側で即座に無効化し、クライアントはローカルキャッシュを更新する。 |
| 鍵の有効期限 | SPK は 30 日程度で自動更新し、古い鍵は削除する。 |
| 永続化 | Session データは端末暗号化ストレージに保存し、復元時はカウンタを検証して不整合を防止する。 |
プロトコル層の構造:Session・Chain・Message キー
Signal の内部では Session オブジェクト が全鍵情報と状態遷移を一元管理します。本節では主要フィールドと更新タイミングを解説し、実装者が注意すべきポイントを整理します。
Session オブジェクトの概要(導入)
Session は X3DH によって生成された Root Key と、Double Ratchet が派生させる Chain Key・Message Key の状態を保持します。これにより、メッセージ送受信時に必要な鍵が即座に取得でき、再計算コストを抑制できます。
主なフィールド
| フィールド | 内容 |
|---|---|
ratchetKeyPair |
現在の DH 鍵ペア(公開鍵・秘密鍵)と送受信カウンタ。 |
chainKeySend / chainKeyRecv |
それぞれ送信用・受信用の最新 Chain Key。 |
msgNumSend / msgNumRecv |
使用済み Message Key のシーケンス番号。 |
rootKey |
X3DH が導出した後、DH ラットチェットで更新される根鍵。 |
鍵生成・破棄のタイミング(導入)
以下は Session のライフサイクルにおける主要イベントです。
- セッション開始
- X3DH により
masterSecretを取得 →rootKey = KDF(masterSecret)。 -
初期 Chain Key (
chainKeySend,chainKeyRecv) を派生させ、カウンタを 0 に初期化。 -
メッセージ送信
- 現在の
chainKeySendから Message Key を KDF で導出し暗号化。 -
暗号化後、
chainKeySend = HMAC_SHA256(chainKeySend, "Ratchet")と更新。 -
メッセージ受信
-
chainKeyRecvから Message Key を復号に使用し、同様に Chain Key をハッシュ更新。 -
DH ラットチェット(再鍵交換)
- 新しい DH 鍵ペアを生成し相手に送付 → 受信側の公開鍵と組み合わせて
rootKeyを再計算。 - 再計算後、双方で新たな Chain Key を派生させ、カウンタをリセット。
実装上の注意:セッション情報は端末ロック時でも安全に保存できるよう、OS が提供する暗号化ストレージ(iOS の Keychain、Android の EncryptedSharedPreferences 等)を利用してください。
オープンソースコードと第三者監査
Signal の暗号実装はすべてオープンソースで公開されており、外部研究者が自由にレビューできます。本節では主要リポジトリと、信頼性を裏付ける独立監査の概要を示します。
GitHub リポジトリ構成(導入)
Signal の公式実装は libsignal-protocol 系列として以下のモジュールに分割されています。
libsignal-protocol-java:Java/Kotlin 向け。Android アプリやサーバー側の参考実装。libsignal-protocol-rust:Rust 言語版。高速かつメモリ安全性が特徴。signal-service-lib:Signal サービス(メッセージキュー・プッシュ通知)との連携ロジック。
各リポジトリは Pull Request ごとに自動テスト(CI)が走り、コードレビューは多数のコミッターが行うため、高い品質が保たれています。
WIRED による独立監査(導入)
2023 年 5 月、WIRED が実施した外部監査レポート[^3]では、Signal の暗号実装全体に対して次の点を評価しました。
- 脆弱性の有無:コードベース上でゼロデイ脆弱性は検出されず、既知の暗号アルゴリズム(Curve25519, AES‑GCM, HKDF)も正しく使用されていることが確認された。
- 実装上の安全策:メモリ安全性(Rust 実装)、タイミング攻撃への対策、鍵削除の徹底などが評価ポイントとして挙げられた。
この監査結果は Signal の「検証可能」かつ「堅牢」な暗号化サービスであることを客観的に裏付けています。
他メッセージングアプリとの暗号比較とプライバシーポリシー
Signal が提供するエンドツーエンド暗号は、他の主流メッセンジャーと比べても独自性が高いです。本節では iMessage と WhatsApp の暗号方式を概観し、データ保持方針の違いを整理します。
暗号方式比較表(導入)
以下の表は 2024 年時点で公表されている情報に基づき、主要なセキュリティ特性をまとめたものです。
| 項目 | Signal | iMessage | |
|---|---|---|---|
| 鍵交換方式 | X3DH + Double Ratchet(オープン) | Apple 独自公開鍵暗号(非公開) | Signal プロトコル (2021‑01 に導入)【^4】 |
| 前方秘匿性 (FS) | あり(Double Ratchet) | 部分的に実装(Apple が内部で管理) | あり |
| 後退秘匿性 (PCS) | あり(DH ラットチェット) | 未公表 | 限定的(サーバー側のキー更新は不透明) |
| メタデータ保存 | 電話番号と最終接続時刻のみ、数秒保持 | Apple ID・連絡先情報を長期保存 | Facebook がメッセージ送受信ログやデバイス情報を収集 |
WhatsApp の暗号プロトコル採用時期(導入)
WhatsApp は 2016 年 2 月に公式ブログで「Signal プロトコルをベースにしたエンドツーエンド暗号化」を開始したと発表しています【^4】。このタイミング以降、テキスト・画像・動画すべてが同様の暗号方式で保護されるようになりました。
Signal のデータ保持方針(導入)
Signal は「最小限のメタデータだけを一時的に保持」する設計です。公式サポート記事[^2] によれば、具体的な取り扱いは次の通りです。
- メッセージ本文:暗号化された状態で数秒〜数分間キューに残すだけで、配信後は削除。
- 電話番号・最終接続時刻:送受信相手を特定するために必要最低限の情報として、一時的に保存されるが永続的なログは残さない。
- SMS 検証コード:認証目的で平文で送信され、数分以内に破棄される。
このポリシーにより、政府や企業からのデータ要求に対して「保有データが存在しない」ことを根拠に拒否できる点が大きな差別化要因となります。
参考文献・リンク
[^1]: Signal Protocol Documentation – “Message Delivery and Storage”. https://signal.org/docs/
[^2]: Signal Privacy Policy (2024). https://signal.org/legal/privacy-policy/
[^3]: WIRED, “The Secret Lives of Signal’s Code” (2023). https://www.wired.com/story/signal-code-audit/
[^4]: WhatsApp Blog, “End-to-end encryption is now default for all chats”. (2016-02-18). https://blog.whatsapp.com/end-to-end-encryption