はじめに
この記事は、 DMMグループAdvent Calendar2021の23日目の記事となります。本日担当するのは、合同会社DMM.comのCTO室 兼 VPoE室で働いております、 Android エンジニアの釘宮( @kgmyshin )です。
9日目の記事で古典的暗号化アルゴリズムについて触れた記事があったので、自分もそれにあやかった記事を書いてみます。
この記事のゴール
この記事では本当はエニグマの仕組みについて説き、それをプログラムで書くならどういう風にコードに落とし込みますか?みたいな話ができればいいなと思っていました。
ただそれを書ききるには、大体30ページ分くらいの薄い本ができてしまうため、ちょっと余白がたりません。そのため、この記事ではエニグマの仕組みをサラッと紹介した後にみんなもエニグマをソフトウェアシミュレーションしてみよう!と投げかけるところまでとしたいと思います。実際にコードに落とし込むまではしないにしても、昔の暗号器がどういう原理だったのかを知ることは、エンジニアをやっていくうえで何か役に立つ可能性が…あるかもしれません。
そもそもエニグマに興味を持ったきっかけ
きっかけはこの映画でした。DMM動画でも見れます。
この映画は、一言で言うと「アラン・チューリングがエニグマの暗号をどうやって解読したのか」という話です。これを見て熱狂冷めなかった私はついつい自分もエニグマの内部構造について知りたいなと思ったのがきっかけでした。アラン・チューリング自体が我々ソフトウェアエンジニアにとっては歴史的に重要な人物であることは間違いないですし、単純にエンジニアとって楽しめる内容だったのでお勧めでございます。
エニグマについて
エニグマとは一言で言うならば、ローター式の暗号器です。(画像は Wikipedia からパブリックドメインのものを引用しています)
使い方は本当に簡単です。 キーボードがあるので、そこに好きな文字をタイピングするだけです。例えば 「 HELLO 」とタイピングすると、ランプボードが「 MFNAB 」の順に光ります。この「 MFNAB 」が暗号化された文字列です。
では、どう複合するのかというと、このタイピングしたエニグマ、もしくは同じ 設定 のエニグマを用意して、キーボードに「 MFNAB 」とタイピングすれば、「 HELLO 」の順でランプボードが光ります。以上で複合完了です。簡単。
また、「 HELLO 」の「LL」の部分に着目してほしいのですが、これは暗号化された文字列を見ると「NA」になっています。タイピングするたびにローターが周り換字ロジックが変わるため、同じ文字を打ち続けても同じ文字にならないために複合化されにくいというメリットがあります。
エニグマは、主に次のパーツから構成されています。
- キーボード
- プラグボード
- ローター
- リフレクター
- ランプボード
先ほど、同じ設定のエニグマであれば複合できるとサラッと書いたのですが、設定は次のものを指します。
- ローターの並び順: ローターは複数種類あり、それらを並び替えて使います
- ローターの初期オフセット: それぞれをカチカチしておきます
- プラグボード: (今回は説明を省きます)
下記はキーボードから入力された文字がどのように変換され、最終的にランプボードにたどり着くのかを示した簡単な図です。
エニグマの中でも重要なパーツである、ローターとリフレクターについて説明していきます。
ローターについて
ローターについて説明していきます。
(画像は Wikipedia からパブリックドメインのものを引用しています)
ローターは次の機能を持っています。
- 入力を受け取り文字を換字する
- 自信が26メモリ進む(1回転する)と、自信に連結している次のローターを1メモリ進める
②はともかく、①の換字の仕組みについて説明していきます。
例えばABCDEFGHIJKLMNOPQRSTUVWXYZ
が ZYXWVUTSRQPONMLKJIHGFEDCBA
に対応している換字テーブルを持っているローターがあるとします。
ローターの設定が A
で設定されているときに、ひとつ前のパーツから A
という文字列が送られてきたとき、このローターは Z
を次のパーツへ送ります。この状態で B
が送られてきたときはY
を次のパーツへ送ります。
では入力値ではなく、設定値を変えた場合はどうででしょうか。 ローターが B
で設定されている場合に A
が送られたときは Y
を次のパーツへ送ります。この状態で B
が送られた場合は X
を次のパーツへ送ります。
どういう仕様なのかを説明します。 送られてくる文字や設定値は便宜のためA
からの距離という数字で表していきます(Aは0, Bは1, Cは2...)。 A
(0)で設定されている場合に B
(1) を送ったら Y
を次のパーツへ送るのは、 ZYXWVUTSRQPONMLKJIHGFEDCBA
の 1
の場所が Y
だったからです。 設定値についても説明します。設定値は換字の際に入力の数字に足すものとして扱われます。そのため、B
(1)を設定しているローターに B
(1)を渡すと ZYXWVUTSRQPONMLKJIHGFEDCBA
の ( 1 +1 = 2 )の場所である X
を送るという原理です。
ローターの説明はこの辺りで終わりにしたいのですが、まだ続きます。 次のパーツへ文字を送った後、次のパーツからまた信号がかえってきて、ローターはそれを受け取り、ひとつ前のパーツへ返却しなければなりません。それが最終的にランプボードに表示されるからです。
次のパーツから文字を受け取った際の振る舞いはどうなるでのでしょうか。 これは先と換字テーブルが逆になるだけです。設定 A
(0) の時に、次のパーツから A
(0) の入力があった場合は、ZYXWVUTSRQPONMLKJIHGFEDCBA
での A の位置は 25
になるので、 ABCDEFGHIJKLMNOPQRSTUVWXYZ
の 25
の Z
を前のパーツに戻します。
と、このようにして ローターは「ひとつ前から受け取った信号を変換して次のパーツへ渡し」「次のパーツから返ってきた信号を変換して前のパーツに戻す」ことで換字を実現しているわけです。
リフレクター
リフレクターはA-Zの26文字すべてが2文字のペアになるように配線されているパーツです。 例えば、BとVがペアになっている場合、Bに入力があれば、Yが出力されます。逆にYの入力があれば、Bが出力されるというパーツです。
この反転性がエニグマに複合機能をもたらしています。まず、リフレクター自体に反転性があるのは先も示した通りですが、例えば ABC
と入力して DBG
と出力された際に逆に DBG
と入力すれば ABC
と出力されるのは自明です。
詳細は省きますが、ローターには「表からEという入力があってVという出力をしたのなら、裏からVという入力があれば表からEという出力をする」という性質を導くことができます。
この性質とリフレクターの性質を合わせると、ローターが含まれても複合することが可能になることがわかると思います。つまるところ、⼊⼒・出⼒の⽅向性は関係なく、リフレクターの存在によってローターを含めても全体ですべての⽂字はペアっているのです。
まとめ
エニグマはまとめると、以下の二点でポイントかなと感じています。
- 換字のロジックはローターが複数あり、かつ、タイピングするたびに換字テーブルが変わる(ずれる)ことで複雑な暗号を作ることに成功している
- リフレクターの存在によって、同じ設定さえ作れば複合が本当に簡単にできてしまう
以下のリポジトリに私が前に書いた30ページほどの薄い本のPDFがあります。 この本ではもう少し他のパーツなどに触れたうえで、実際にコードに落としてコマンドラインエニグマシミュレータを作ってみようという内容になっています。 (今コードを見直したら修正したい箇所もありますが)興味ある方いれば読んでみてください。