(AES-)CBC, GCM と同一の初期化ベクトル

Published: 2023/8/12


データベースの暗号化を行いたい、となったときなどには、基本的に AES-256-GCM を使うのが、現時点では最もポピュラーだと思われる。 その実、 Rails 7 から導入された database encryption の機能においても、 non-deterministic mode におけるデフォルトの暗号化アルゴリズムは AES-256-GCM である、との記述がある。

AES-GCM は block cipher (ブロック暗号) である。 block cipher では、固定幅の暗号的な全単射が与えられたとき(AES-GCM の場合には、 AES で 128 bit の暗号的全単射)に、それをどのように利用して任意メッセージを暗号化・復号化するかを考えていく。 実用上で使われるそのような手法として、有名なものは CBC と GCM がある。

CBC においても GCM においても、初期化ベクトル(Initialization Vector; 以下、 IV)を必要とする。 これは、暗号化において同じ鍵を使いまわす際には、何かしらのランダム性を与えておかないと、似ていた、ないし同じであった平文データの構造が、暗号化した後において保存されてしまうこと回避するために必要となる。 分かりやすく言うと、別々の人が入力したデータを保存するとして、その元々のデータが(たまたま)同じであったとき、暗号化文は同じになって良いのだっけ? への対策となる。 (同じである以外でも、部分的に似ていると、それが暗号化後の文に反映されてしまったりする)

IV それ自身は、暗号化などせず、 IV と鍵を用いて暗号化されたデータと一緒に保存してしまって良い。 あくまで、平文に対してランダム性を加えて元々の平文同士の関係性を暗号化した後において保存させないために利用される。 なので逆に、もし同じ IV を用いて暗号化されたデータが2つ存在してしまったとき、それは IV のやりたかったこと(セキュリティの担保)を何かしら損ってしまうことになる。

では、具体的に同じ鍵を使い回してしまった際に、何が発生するのかということについて、調べた結果をまとめていく。 この情報は、基本的に以下のページの内容を参考にしている。

CBC で同じ IV を使った時のリスク

同じ IV を利用すると、元々の平文の関係性が漏れてしまう。 分かりやすくは、先頭の 128bit が同じ文字であれば、それは同じ IV を使うかぎりにおいて、暗号化後も同じになってしまう。 (上記で最初に載せた、暗号初期化ベクトルは役に立つのか? のページに具体例がある)

また、 IV が何になるか予測できて、かつ、攻撃者が自身の平文を暗号化させることができる場合、その暗号化処理を oracle として用いることで、他の暗号化文の解析ができてしまったりする。

具体的には上記参照。 メッセージの盗聴者があるメッセージ ColdC_\mathrm{old} と、それに対応する IVold\mathrm{IV}_\mathrm{old} を得ていたとして、その本来の平文が PoldP_\mathrm{old} なのではないか、と推測したとする。 そして、次の IV が IVnew\mathrm{IV}_\mathrm{new} であると推定できていたとする。 ColdC_\mathrm{old} の平文が PoldP_\mathrm{old} であったかどうかは、以下の平文 PnewP_\mathrm{new} をシステムに投げて暗号化して、その結果 CnewC_\mathrm{new} を観測することで、検証が可能となる。(簡単のため、次の IV の予測値 IVnew\mathrm{IV}_\mathrm{new} は、実際にその通りのものが利用されたものとする; 違ったとしても、 IV 自体は特に暗号化されずに通信で流れて傍受できるものと想定されるので、誤った検証のデータを利用して突き進んでしまうことはない)

Pnew=PoldIVoldIVnewP_\mathrm{new} \coloncolonequals P_\mathrm{old} \oplus \mathrm{IV}_\mathrm{old} \oplus \mathrm{IV}_\mathrm{new}

実際、真に ColdC_\mathrm{old} の平文が PoldP_\mathrm{old} であった場合に、以下が成立し、返ってきた暗号文が解読したかった暗号文と一致するため、検証が成功したことを確認できる。

Cnew=AESCBC((PoldIVoldIVnew)IVnew)=ColdC_\mathrm{new} = \mathrm{AESCBC}((P_\mathrm{old} \oplus \mathrm{IV}_\mathrm{old} \oplus \mathrm{IV}_\mathrm{new}) \oplus \mathrm{IV}_\mathrm{new}) = C_\mathrm{old}

この検証は、繰り返し行っていくことで ColdC_\mathrm{old} の平文の候補がそこまで多くない場合には、総当たりで PoldP_\mathrm{old} の割り出しが可能となる。

GCM で同じ IV を使った際のリスク

CBC の場合よりもヤバいというのが一般的な見解。

まず、同じ IV を使って暗号化した2つのデータが見つかった場合、その元の平文の xor した値が求められてしまう。 これは、 GCM がカウンターモードの block cipher であることに起因する。 参考: GCM モード

また、 GCM には暗号化とともに署名の機能もあるが、その内容を改竄できるようになる。 この改竄が何故可能なのか、については、同じ IV を利用して計算された authtag 2つの xor を取った後、結果を因数分解すれば、 GCM の署名機能に利用される鍵 H (!= AES など、ベース暗号化関数の鍵!) が求まるから。らしい。 この改竄可能性については、下記参照。

基本的に、 GCM では IV (というより、実態としては nonce) の再利用は超ヤバいので、以下のうちいずれかを行わなければならない。

  1. IV は毎回ランダム生成するが、うっかり同じ IV が生成される可能性が十分に低い範囲での利用に留める。割と十分な数の IV を生成できるっぽいが。
  2. nonce なので、毎回 +1 して、絶対に同じ IV は使わない

いずれにせよ、 IV がある程度利用されたら、元々の鍵自体を入れ替えて rotation する必要がある。

CBC と違い、 GCM では次に利用される IV が予測されても、問題にはならない。


Tags: security