エニグマ(初期版)をVC#で作ってみた
社会の情報化・電子化が進み、暗号化というものは不可欠な存在になってきました。
パスワードだとか、あるいはもっと重要な企業間、国家間の機密情報が平文のままやりとりされていては堪ったものではありません。
実際に利用されている暗号化の方式としてはRSA暗号などが有名どころでしょうか。
さて、ここまでは現代での話をしましたが、暗号化というのは昔から重要なものでした。特に、戦争なんかの場合には。相手に作戦などが盗聴されていようものならそれこそ死活問題です。
というわけで、今回の主役は第2次世界大戦中に連合軍を苦しめたデバイス、エニグマ(Enigma)です。
そもそもエニグマって何?
エニグマ、その名前こそ聞いたことはあれど、実際にその仕組までは知らないという人がほとんどかと思います。自分もそうでした。
といっても、私もまだ詳細な解説をできるレベルではないので、私が実際に参照したサイトを紹介しておきます。
特に、下のQiitaの記事の解説は大変参考にさせていただきました。
下の実際にコーディングをするところは読んでないのですが(自分で0からコードを書きたかったので)、とてもわかり易い記事だと思います。
一応ざっくりな説明をしておくと、エニグマの暗号化方式はシーザー暗号の亜種のようなものになっています。(換字式)
どういうことかと言うと、シーザー暗号では全てのアルファベットを決められた文字数分だけシフトして暗号化を行いますが、エニグマは暗号化の際文字ごとにシフトする量を増やしていきます。
ただしこれでは26文字ごとに周期性が生じてしまうので、エニグマでは回転させるローターと呼ばれる部品の数を複数にすることで26の整数乗が周期となるようになっています。
(初期のエニグマは周期が263=17576文字)
また、暗号文をエニグマに入力すれば平文が得られるように作られています。(リフレクターという部品のおかげ)
うーん、すごい仕組みです。なんか群論チック。
まああとの解説は各自調べていただくとして、実際にコーディングしていきたいと思います。
実際にエニグマを書いていく
フォームアプリケーションということで、とりあえずウィンドウの方を作ってみます。
スクランブラーの並び替えは最終的に外部からできるようにしたのでそこは使用してませんが、それ以外は使っていきます。(いまいち左右非対称だったりするのは気にせず)
真ん中の3つのコンボボックスはスクランブラーの初期位置設定用です。
暗号化のコーディング
さてコードを書いていきます。とりあえず暗号化ボタンを押したときのイベントにずらずら書きましょう。
private void ButtonEnc_Click(object sender, EventArgs e) { indexOf = 0; plugTable = new int[12]; string orig = textBoxOrig.Text; int lg = orig.Length; string output = ""; roterA = comboBoxRoterA.SelectedIndex; roterB = comboBoxRoterB.SelectedIndex; roterC = comboBoxRoterC.SelectedIndex; bool flag = radioButtonPlug.Checked && textBoxPlug.Text.Length == 12; if (flag) CreatePlugTable(); string temp; int tempStr; for (int i = 0; i < lg; i++) { temp = orig[i].ToString(); tempStr = EncodeToNumber(temp); if (tempStr < 0) return; if (flag) tempStr = ConvertInPlug(tempStr); tempStr = ScrambleInRoterA(tempStr); tempStr = ScrambleInRoterB(tempStr); tempStr = ScrambleInRoterC(tempStr); tempStr = Reflecter(tempStr); tempStr = ReturnInRoterC(tempStr); tempStr = ReturnInRoterB(tempStr); tempStr = ReturnInRoterA(tempStr); if (flag) tempStr = ConvertInPlug(tempStr); output += alphabets[tempStr]; indexOf++; } textBoxOutput.Text = output; }
スパゲティコードかもしれませんが、とりあえず処理のアウトラインを説明します。
一番上のindexOf変数は今何文字目かをカウントする変数です。ローターの回転用ですね。
プラグボードは一旦おいておき、その次にあるorig変数が平文を保存しています。
あとは文字数分だけ暗号化処理をforループです。
中身はアルファベットを数字へ、(プラグボード、)ローター3つで処理を行った後リフレクターを通ってまたローターを帰ってくる、これは本家のエニグマと同じ仕様です。最後に数字からまたアルファベットへ変換しています。
(多分違うのは各ローター内での文字の対応関係だけ?)
あとはスクランブラーの処理。非常に単純です。
private int ScrambleInRoterA(int input) { int rot = (input - indexOf - roterA + 260) % 26; return tableA[rot]; }
これはローターAの処理ですが、B・Cでもほぼ同じ処理で回転数が違うのみです。また帰ってくるときの処理はこれの逆を行います。
tableAというのは26個の数字がそれぞれどの数字に対応するのか書いた配列です。簡単に言うと定義域・値域が共に0-25の整数の写像(のうちの1つ)で、全単射のものです。
+260というのは剰余を求めるときに割られる数が負の数になることを防いでいるだけなので本質には関係ありません。
自分で見てもひどい図ですが雰囲気が伝わればいいです。(伝わるのか?)
こんなような性質を満たす写像のうちいくつかを、スクランブラーの配列として採用します。(実際配列を作るときにはPythonでshuffle()を使いました)
リフレクターについて
エニグマは暗号文をもう一度暗号化すると平文に戻るので、こういう仕様になるようにリフレクターを作る必要があります。言葉にすると難しそうですが、実際はこれで動きます。
private int Reflecter(int input) { return (input + 13) % 26; }
合同式大活躍ですね。
仮完成
ここまでコードを書いてスクランブラーとリフレクターができれば、とりあえずエニグマは動きます。プラグボード無し版エニグマですね。
ですがこの状態だと非常に換字のパターンが少なく、先程も書いたように17576通り、またスクランブラーの交換を考慮しても6倍で高々6桁通りです。
これではまだ総当りできるレベルです。この問題を解決するために、プラグボードが必要になります。
プラグボードも書く
どうやってプラグボードの設定ができるようにしようかと悩んでいたのですが、フォームでアルファベット12文字を入力してそれを先頭から2文字ずつペアにして交換、というような仕組みにしてみました。まったくもってGUIでない
こんな感じで変換テーブルを作って、交換はインデックスを±1すればいいので楽です。
private void CreatePlugTable() { string temp; int tempIdx; for (int i = 0; i < 12; i++) { temp = textBoxPlug.Text[i].ToString(); tempIdx = EncodeToNumber(temp); plugTable[i] = tempIdx; } }
エニグマができた!
ということでVC#で動くエニグマの完成です。これで友達に暗号文送って遊び放題。
というか、スクランブラーの配置は唯一無二のものですからほぼ解読は無理に近いですよね。友達からしたらただの怪文書送ってくる奴。
書いてて実際に動いた!ってなったときはめちゃくちゃテンション上がりましたね。エニグマが作れるなんて中二病全開にならざるをえない。
まとめですが、今回作ったエニグマの換字表パターンはスクランブラー1つに26文字、取替可能、またプラグボードまで作ったので全部で263 * 3! * (26文字から2文字ずつ選んで6つ組をつくるパターン数)になります。
最後のやつは計算すると72兆くらいになるので(この時点でやばい)、全部合わせると7622580070385280000通り!!
これは流石に総当りできないですよね。結論:プラグボード強し。