今年のGoogle Capture The Flagにはkijitora というチームで出ました。2022年のGoogle CTFに合わせて結成された連合チームで、このときはまだ私もCTFやっているという感じだったので末席に名を連ねていました。このときにdiscordサーバにjoinしていたことで今年もしれっと参加が許されて、今年は真面目にやったのでDESFUNCTIONALという問題とIDEAという問題を協力しながら解きました。IDEAは結局4チームしか解けてない難しい問題だけど粘った結果解けたので、writeupです。
- TL; DR
- 問題概要
- KとK⊕2103の関連鍵攻撃 (k_18, k_20の導出)
- KとK⊕2127の関連鍵攻撃 (k_19, k_21の導出)
- 32bit全探索によるk_16, k_17の特定
- 32bit全探索による暗号鍵の復元
- コードすべて
- 感想
TL; DR
スーパーヒューリスティックで粘って鍵をちょっとずつ求めて、残り32bitになったら全探索
問題概要
- IDEAというブロック暗号が実装されている。通常は8.5ラウンドのところ、3.5ラウンドのみの実装になっている
- サーバに接続すると次のことができる。コストが2024を超えるか、サーバに接続後512秒が経過したら挑戦失敗で、暗号化に使われている128ビットの鍵を当てることができればフラグがもらえる
- 暗号化 : コスト1
- ビット反転:コスト=反転するbit数*100
- 鍵の好きなbitを反転できる。以後の暗号化では反転操作を行った鍵が使われる
IDEA について
wikipediaに載っているのでこれを参照されたい。以下ではこのwriteupで使う話だけを説明する。
鍵が128ビット、1ブロックが64ビットのブロック暗号。暗号化処理中は平文、鍵を16ビット単位の4つ組(鍵は6つ組)として扱い、KAという操作とMAという操作からなるラウンド操作を繰り返し行うことで暗号化する。ラウンド目の入力を
と表すことにし、
ラウンド目で利用されるラウンド鍵を
と表すことにする*1。ラウンド鍵の導出についてはあとで説明する。
KAは次のような操作である(出力を とする)
MAは次のような操作である(出力は、次のKAの入力となるのでと書いた)
ただしとして、
である。
ここで、 は
上での加算、
は
上での乗算*2、
はビットごとの排他的論理和である。
また、暗号化の最後にはハーフラウンドがある。これはKAと酷似したKA'という処理によって行われる*3。
KA':
ラウンド鍵はマスター鍵の適当なビットスライスである。説明が面倒なので論文*4から表を引っ張ってくるとこういう感じ。非常にシンプルで、ラウンド鍵がわかればマスター鍵もすぐ求まる。

また、ラウンド鍵については0-indexedな通し番号とも表すことにする。つまり
という具合である。
KとK⊕2103の関連鍵攻撃 (k_18, k_20の導出)
鍵の差分を打ち消すような差分を持つ平文を渡す
ビット反転という鍵の好きなビットを反転させる操作があることから、これを使うのだろうと予想して論文を探すとIDEAに対する関連鍵攻撃を説明している論文*5が見つかった。その論文によると3.5ラウンドのIDEAでは、鍵で平文
を暗号化した結果と鍵
で平文
を暗号化した結果の差分からいくつかのラウンド鍵を求めることができるらしい。
鍵から導出されたラウンド鍵
と鍵
から導出されたラウンド鍵
を比較すると、鍵の値が異なるのは
の2ペアだけで、その値もそれぞれ1bitずつしか違わない。
そして、が作用するのは平文の2ブロック目*6に対してであり、KAの処理をみればその作用は
である。これは
上での加算にすぎないので、鍵の加算の1bit文を打ち消すような差を持つ平文を暗号化させれば1ラウンド目の暗号結果が一致する。
このような結果になる平文を鍵
で暗号化すると続くラウンドでは次のようになる
k_18の導出
さて、ここで2つの暗号文について、 (すなわち
がわかっているという仮定をおいた状態で、1ブロック目と3ブロック目の差分(XOR)の最下位bit (LSB)を比較してみる。
まず1ブロック目について(①)。これはだが、いま
(
)はわかっている前提なので逆数をかけて取り除いて良い。ついでに
は開いてしまって
がわかっている。 なのでこれのXORをとると
が得られる。LSBをとって①=
続いて3ブロック目について(②)。 だがこれも
を開いて
である。
あとでLSBをとることを考えると、と
は同一視できて*7
としてもよい。
かつ
より、(LSBとって)XORを取ると②=
。
つまり、がわかっているとき、①=②が成り立つ。
は16bitの値なので全探索可能だから、①=②が成り立つような値を探せばそれが
である、ということになる。
当然①, ②の式の値は0か1しかないので、探索中にはでない値についても①=②が成り立つことがあるが、この性質は(1ラウンド目の暗号化結果がおなじになるような)どんな
のペアについて成り立つので、そのような平文の組にたいする暗号文の組を複数個あつめて、すべてのケースについて成り立つ値を
として採用すれば良い。
k_20の導出
がわかれば同様の議論で
が導出できる。さっきはLSBを考えたが、今回はLSBを取らずに単にXORを取ることを考える。
1ブロック目については単にが得られる。3ブロック目については
(
) がわかっているという前提をおけば、この値を引いてXORを取れば、
となるはずである。つまり、正しい
について
が成り立つ。これはLSBを取っているわけではないが、加算という処理が影響するビットの少なさから誤った
の予想についても成り立つことがあるため、やはり複数の組すべてで成立するような値を採用すると良い。それでも2通りでてくるんですがまあ1/2であたりなので良しとする
KとK⊕2127の関連鍵攻撃 (k_19, k_21の導出)
続いて、鍵にたいして
としたときの関連鍵攻撃を考える。
このとき、差分が現れるラウンド鍵はの3ペアである。先ほどと同様に、1ラウンド目の暗号結果が同一になるような平文のペア
を考えたいのだが、今回は1ラウンド目で差分があるラウンド鍵が乗算に使われていて、
に対して計算して
を求めることはできない。ただし、
の2ブロック目から4ブロック目は
と同じとしたうえで1ブロック目の値を
通り試せば、かならずそのような
が一つ存在する*8。今回の問題設定では
通り試すことは不可能だが、後述のように誕生日攻撃でこのような
のペア(good pairと呼ぶことにする)を得ることができる
誕生日攻撃によるgood pairの収集
あるにたいしてgood pairの片割れとなるような
が得られる確率は
だが、いくつもの
に対して、そのいずれかとgood pairになるような
が得られる確率はそれよりもずっと大きい*9。この性質を利用して、2, 3, 4ブロック目を固定し1ブロック目を適当に変化させた平文を鍵
のもとでいくつも暗号化しておき、
のもとでも同様に1ブロック目だけを変化させた平文をいくつも暗号化し、あとでgood pairになっている組があるかを確かめることで、現実的な回数の暗号化オラクルの利用でgood pairとなるような組を得ることができる。
あるがgood pairになっているかの判定は、次のようなお気持ちでやる。
これでgood pairとなるようなが求められる。
この方法でgood pairを求めると、実際にはgood pairではないがgood pairと判定されてしまうケースもそこそこある。実験した感じでは、体感50%くらいの偽陽性がある。しかしもっと良い方法は思いつかなかったのでこれで頑張った
中間一致攻撃によるk_19, k_21 の特定
さて、これでgood pairが(いくつか)求まった。ここで更にもわかっていると仮定する。
このとき2ブロック目について、 から
と
をつかって
が計算できる。
4ブロック目についても同様に、となるから、正しい
の組については
が成り立つ。つまり32bitを全探索すれば
が求められる。
ただし、32bitも全探索する必要はなくて、について全探索して、ある
(の候補)のときの
の値をキーとする連想配列を用意しておけば、
の値を全探索しつつその
について、
が同じ値になったときの
を引っ張ってくる、という中間一致攻撃ができて、これなら16bit全探索が2回で済む。
のペアについては候補が複数組でてくることがあるが、うまくいくケースでは数組に収まるので、それらの組を全部試すか適当に選んだ組が正解であることを祈りながら何度も挑戦すればよいと思って妥協している。
32bit全探索によるk_16, k_17の特定
ここまでで得られた情報をもとに、は全探索して求める。ここまでで4ラウンド目(3.5ラウンド目)の値は一通り逆算できるようになっていて、
や
が求められるようになっている。これらの値と
から
を求め、
と
が一致するか、
と
が一致するかを複数ケースについて確認すれば正しい
が求められる。
32bit全探索による暗号鍵の復元
これで、の6個のラウンド鍵がわかり、マスター鍵の16 * 6 = 96bit があきらかになった。残りは32bitなので、全探索すれば良い。単純に鍵の残りの32bitを探索しながら平文を暗号化し、サーバと同じ暗号結果になるようなケースが見つかれば、そのときの鍵が答えになる
コードすべて
以下のgistに貼っておきました。確実性がめちゃめちゃ低いので祈りまくる事になってます。creditの消費量はだいたい1120程度だったようです。
感想
難しい問題を解けてよかったです。頭がおかしくなるかと思いました。writeupを書くのが大変すぎます。こんな大変な問題を作った@mystizと@deuteriumを尊敬しているし、感謝しているし、うらんでます。一緒に取り組んでくれたkijitoraのメンバーのみなさんと、特にこの問題に一緒に頭を捻ってくれたy011d4さんににも感謝です。
あと、この長いwriteupを最後まで読んでくれた方にも。もし間違っているところや曖昧なことを言ってるところを見つけたら教えて下さい。