AEAD(認証付き暗号)の設計原理と nonce 再利用の破壊力
なぜ暗号化だけでは守れないのか。AEAD は秘匿と改ざん検知を一体化した道具で、その内部構造と nonce 再利用が招く致命傷を原理から押さえれば、実装事故を確実に避けられる。
- 1.AEAD は機密性(暗号化)と完全性(認証タグ)を一体で提供する。GCM も ChaCha20-Poly1305 も「ストリーム暗号 + 一発認証用 MAC」という同じ骨格を持つ。
- 2.Encrypt-then-MAC が正しいのは、復号前に改ざんを弾けるから。MAC は暗号文に対して計算し、検証失敗なら平文を一切出さない。順序を間違えると padding oracle 等の事故になる。
- 3.GCM/Poly1305 で nonce を再利用すると、平文の XOR が漏れるだけでなく認証鍵そのものが計算で復元され、任意の偽造が可能になる。これを救うのが誤用耐性の AES-GCM-SIV。
AEAD が解く問題:暗号化と認証の不可分性
暗号化が守るのは機密性だけで、改ざん検知は別の仕組みが要る——これを一つの操作に束ねたのが **AEAD(Authenticated Encryption with Associated Data/認証付き暗号)**です。AEAD は2つの保証を同時に出します。
- 機密性:暗号文から平文が読めない。
- 完全性・真正性:暗号文(と付随する平文ヘッダ)が改ざんされていれば、復号時に必ず検出して拒否する。
入力は「平文」「鍵」「nonce(一度限りの番号)」「AAD(Associated Data=暗号化はしないが認証はしたい付随データ。例:パケットヘッダ)」。出力は「暗号文」と「認証タグ」です。復号側はタグを再計算して照合し、一致しなければ平文を1バイトも返さないのが鉄則です。
平文を隠すだけのストリーム暗号は、暗号文の特定ビットを反転させると平文の同じビットが反転します(後述)。攻撃者は中身を読めなくても狙った改ざんができてしまう。AEAD はタグ検証でこれを弾くため、現代の通信(TLS 1.3 は AEAD のみ)やディスク暗号で標準になりました。
なぜ Encrypt-then-MAC が正しいのか
暗号化と MAC(メッセージ認証コード)を組み合わせる順序には3通りあり、**Encrypt-then-MAC(EtM)**だけが汎用的に安全だと証明されています。
| 方式 | 手順 | 問題点 |
|---|---|---|
| Encrypt-then-MAC | 平文を暗号化 → 暗号文に MAC | 復号前に改ざんを弾ける。理論的に最も安全 |
| MAC-then-Encrypt | 平文に MAC → まとめて暗号化 | MAC検証の前に必ず復号が走る。padding oracle の温床 |
| Encrypt-and-MAC | 平文を暗号化/平文に MAC を別々に | MAC が平文の情報を漏らしうる。決定性で同一平文がバレる |
決め手は「改ざんされた暗号文を、復号という危険な処理に通す前に捨てられるか」です。EtM ではタグが暗号文に対して計算されるので、検証は復号より前に完結します。タグが合わなければ復号ルーチンを一切起動しない。
逆に MAC-then-Encrypt は、検証のためにまず復号してしまう。この「復号してから判断する」隙が、復号結果のパディング妥当性をエラーの差から読み取る padding oracle 攻撃を生みました(CBC モードの実装事故として有名)。GCM も Poly1305 も EtM の構造を採り、タグ照合は復号データを外に出す前に行います。
タグの比較を if (tag == expected) のような早期 return する比較で書くと、一致するバイト数で処理時間が変わり、タグを1バイトずつ当てるタイミング攻撃が成立します。必ず定数時間比較(constant-time compare)を使うこと。標準ライブラリの AEAD はこれを内部で行っています。
GCM と ChaCha20-Poly1305 の内部構造
代表的な2つの AEAD は、実装の部品は違っても「ストリーム暗号で秘匿し、一発専用の MAC で認証する」という同じ骨格です。
共通の骨格:
keystream = StreamCipher(K, nonce) # 鍵ストリームを生成
ciphertext = plaintext XOR keystream # 秘匿(ストリーム暗号)
tag = OneTimeMAC(authKey, AAD, ciphertext) # 認証(暗号文+AADに対して)
AES-GCM は、AES をカウンタモード(CTR)で回して鍵ストリームを作り、暗号化します。認証は GHASH という、有限体 GF(2^128) 上の多項式評価で行います。AAD と暗号文のブロックを係数とする多項式を、秘密の評価点 H = AES_K(0)(全ゼロブロックの暗号)で評価し、その結果に最終ブロックの暗号をかぶせてタグにします。
ChaCha20-Poly1305 は、ChaCha20 というビット演算(加算・回転・XOR、いわゆる ARX)ベースのストリーム暗号で暗号化し、Poly1305 で認証します。Poly1305 はメッセージブロックを係数、素数 2^130 - 5 を法とする多項式を秘密鍵 r で評価する MAC です。AES のハードウェア命令(AES-NI)が無い環境でも高速で、サイドチャネルに強い設計です。
| 観点 | AES-GCM | ChaCha20-Poly1305 |
|---|---|---|
| 暗号化 | AES(CTRモード) | ChaCha20(ARX系ストリーム) |
| 認証MAC | GHASH(GF(2^128)多項式) | Poly1305(mod 2^130-5 多項式) |
| 速い環境 | AES-NI のある CPU | AES-NI が無い/ソフト実装・モバイル |
| nonce長 | 96bit 推奨 | 96bit |
| サイドチャネル耐性 | 実装次第(テーブル参照に注意) | ARXで定数時間にしやすい |
両者とも MAC は多項式評価で、評価に使う鍵(GCM の H、Poly1305 の r)が秘密であることが安全性の前提です。この前提が崩れる瞬間が、次の nonce 再利用です。
nonce 再利用がなぜ破滅的なのか
GCM・ChaCha20-Poly1305 の安全性は「同じ鍵で同じ nonce を二度使わない」という一点に依存します。これを破ると、機密性と完全性の両方が一度に崩壊します。
第一の漏洩:平文の XOR が露出する
ストリーム暗号は 鍵ストリーム = F(K, nonce) で決まります。鍵と nonce が同じなら全く同じ鍵ストリームが出ます。同じ鍵ストリーム S で2つの平文 P1, P2 を暗号化すると:
C1 = P1 XOR S
C2 = P2 XOR S
C1 XOR C2 = (P1 XOR S) XOR (P2 XOR S) = P1 XOR P2
鍵ストリーム S が打ち消し合い、攻撃者は2つの平文の XORを手に入れます。片方が既知だったり、自然言語のように冗長性があれば、ここから平文を復元できます(crib-dragging)。これは古典的なワンタイムパッド再利用の事故と同じ原理です。
第二の漏洩:認証鍵そのものが計算で解ける
より深刻なのはこちらです。GCM/Poly1305 の MAC は秘密の評価点を使った多項式でした。nonce が同じなら、タグを覆い隠す最終マスクも同じ値になります。ここで攻撃者が2つの (暗号文, タグ) を持つと、タグの式を差し引く形で未知マスクを消去でき、評価点 H(または r)を未知数とする方程式が立ちます。
GCMの概念式(記号化):
tag = GHASH_H(AAD, C) XOR E_K(nonce) # E_K(nonce) が nonce 依存のマスク
同じ nonce の2メッセージで差をとると:
tag1 XOR tag2 = GHASH_H(...1) XOR GHASH_H(...2)
→ 右辺は H の多項式。マスク E_K(nonce) は同じなので消える。
→ H について解ける(GF(2^128)上で根を求める)
H が割れると、攻撃者は任意のメッセージに対して正しいタグを自力で計算できます。つまり完全性が完全に失われ、好きな暗号文を「正規」として偽造できる。鍵 K 本体は出ませんが、認証は無力化されます。これが「nonce 再利用は平文だけでなく認証鍵も漏らす」と言われる理由です。
GCM で nonce をランダム生成する場合、96bit 空間(2^96)の誕生日衝突は約 2^48 メッセージで確率が無視できなくなります。NIST SP 800-38D が同じ鍵あたり 2^32 メッセージを上限とするのは、ここまで衝突確率を十分小さく(2^-32 程度に)抑えるためで、2^32 は「衝突が起きる点」ではなく「超えてはいけない運用上限」です。高スループットなシステムでは、ランダムではなく単調増加カウンタで nonce を割り当てるのが安全です。ただしカウンタはサーバー再起動やマルチプロセスでリセット・重複しやすく、運用が難しい。鍵には明確なメッセージ数上限を設け、超える前にローテーションすべきです。
誤用耐性 AEAD:AES-GCM-SIV の動機
nonce 再利用が「実装ミス一発で鍵まで漏らす」なら、そもそも再利用しても致命傷にならない設計が欲しくなります。それが**誤用耐性 AEAD(Misuse-Resistant AEAD, MRAE)**で、代表が AES-GCM-SIV です。
鍵は SIV(Synthetic IV)という考え方です。通常の AEAD は nonce を外から与えますが、SIV では先にタグ(認証値)を計算し、そのタグ自体を暗号化用の初期ベクトルとして使う。暗号化の入力が「nonce + AAD + 平文」全体から決まるため、平文が1ビットでも違えば鍵ストリームも変わります。
通常GCM: keystream = F(K, nonce) # nonce が同じなら同じ
GCM-SIV: tag = PRF(K, nonce, AAD, P) # 平文まで含めて算出
keystream = F(K, tag) # 平文が違えば tag が違い、streamも違う
結果として、たとえ同じ nonce を使い回しても:
- 異なる平文なら鍵ストリームが異なるので、第一の漏洩(XOR 露出)が起きない。
- 漏れるのは「全く同一の平文・AAD・nonce を二度送ったという事実」だけ(決定性ゆえ同じ暗号文になる)。これは深刻な鍵漏洩とは比較にならない。
SIV は安全側に倒すぶん、暗号化に平文を2回走査する(先にタグ計算、次に暗号化)必要があり、ストリーミング処理には向きません。標準の GCM/ChaCha20-Poly1305 は1パスで速いが nonce 管理が命綱。nonce の一意性を確実に保証できるなら通常の AEAD、保証が難しい(分散・多プロセス・乱数依存)なら GCM-SIV、と使い分けるのが原理に沿った判断です。
まとめ
AEAD は機密性と完全性を不可分に束ねる道具で、GCM も ChaCha20-Poly1305 も「ストリーム暗号 + 多項式ベースの一発 MAC」という同じ骨格を持ちます。Encrypt-then-MAC が正しいのは、危険な復号処理に通す前に改ざんを弾けるから。そして両者の安全性は秘密の評価点を使う多項式 MACに支えられ、ここに nonce 再利用が刺さると、平文の XOR が漏れるだけでなく評価点=認証鍵が計算で解け、任意偽造に至ります。この一発死を避けたい現場のために誤用耐性の AES-GCM-SIV があり、nonce 一意性を運用で保証しきれるかが選択の分かれ目です。
鍵と署名の土台は 暗号の基礎、ハッシュと暗号化の役割分担は ハッシュ化と暗号化の違い、鍵の真正性を保証する仕組みは 公開鍵基盤(PKI) を合わせて参照してください。
セキュリティ Article
AEAD(認証付き暗号)の設計原理と nonce 再利用の破壊力を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
暗号
比較で見る軸
難易度: advanced / カテゴリ: セキュリティ / タグ数: 5
導入後に効く点
Encrypt-then-MAC が正しいのは、復号前に改ざんを弾けるから。MAC は暗号文に対して計算し、検証失敗なら平文を一切出さない。順序を間違えると padding oracle 等の事故になる。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- セキュリティ
- タグ数
- 5
判断チェックリスト
- 自社の用途が「暗号 / AEAD」に近いか確認する。
- 強みである「AEAD は機密性(暗号化)と完全性(認証タグ)を一体で提供する。GCM も ChaCha20-Poly1305 も「ストリーム暗号 + 一発認証用 MAC」という同じ骨格を持つ。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。