見出し画像

雑記:エニグマ暗号機シミュレーターを作ってみた

長らく無沙汰であったが、このたび久しぶりに思い立って記事を書くことにした。内容はタイトルの通りエニグマ暗号機シミュレーターを自作したことである。

世の中にはしっかりしたシミュレーターが既に多くあるというのに、わざわざ正確性が不安な自作シミュレーターを作ったのには理由がある。既存のシミュレーターは実在したエニグマのモデル(Enigma I、Enigma M4など)の再現を行っている。しかし、自分で自由にスクランブラーやリフレクターの内部配線を設定できるシミュレーターは、リサーチ力不足のためか見かけない。そこで自作することにした。
実を言うと、筆者はVRChat(本記事の主題ではないので説明は省略)にて暗号を主体とした謎解きワールドを制作中であり、その中でエニグマ暗号の問題も出題するつもりである。その問題を作るにあたり、シミュレーターが必要になったという次第である。

ではどうやって作るか。プログラムを組もうにも、どうエニグマ暗号機が動くかを知らねば作ることはできない。そのため、まずは自分にとってわかりやすいところでたたき台を作ることにした。それはGoogle スプレッドシートである。今回はスプレッドシート上でのシミュレーター作成を行う。

最初に言っておくと、完成図を先に描いてから作ったわけではなく、作りながら試行錯誤してどうにか動くようにした代物である。そのため洗練されておらず、煩雑であることをご容赦願いたい。

そもそもエニグマとは

本記事執筆時点でエニグマに関する記事は書いていないが、簡単な説明を行うと、機械を使って膨大な数の変換パターンを実現した換字式暗号である。

エニグマの仕組み

具体的な内部構造や動作機構はこちらの動画が詳しい。

以下、主な暗号機の要素について述べる。

キーボード

英: Key board 独:Tastatur
キーを押すとスクランブラーが回転し、電気回路が形成されて電流が流れる。

プラグボード

英:Plug board 独:Steckerbrett
プラグボード上には26のアルファベットに対応するプラグがあり、ケーブルを差し込むことで2つのアルファベットを交換することができる。例えばAとBを交換した場合、Aの電気信号は本来Bを暗号化する経路を伝わり、Bの電気信号は本来Aを暗号化する経路を伝わっていくことになる。
使用者が任意に設定できる部分であり、エニグマ暗号の鍵の1つである。

エントリーホイール(ETW)

英: Entry wheel 独: Eintrittswalze
プラグボードにつながっているワイヤーを束ねてスクランブラーまでつなげるものなのだろうか。よく分からない。
このホイールは文字に何らかの変換を施すわけではないので、シミュレーターには特に組み込まなくてもいい。

スクランブラー

英: Scrambler 独: Walze
ローター(Rotor)とも言う。両面に端子があり、その内部はワイヤーが複雑に配線されている。このワイヤーの配線が換字表を実現している部分である。1文字暗号化するごとに1ステップ回転し、26ステップで1周するので、スクランブラー1つで26通りの換字表を実現している。これを3つ用いることにより26×26×26=17576通りの換字表になる(エニグマのバージョンによっては4つ使う場合がある)。さらに3種類のスクランブラーは並べ替えられるので、場合の数は3!=6倍されて105456通りに増える。後に2種類のスクランブラーを追加したのでスクランブラーの配列方法は5P3=60通りに増加、換字表のパターンは1054560通りにまで増えた(エニグマのモデルによってはもっと増える)。
スクランブラーの配列とその初期位置は使用者が任意に設定できる部分であり、エニグマ暗号の鍵の1つである。

スクランブラーには歯車がついている。キーボードのキーを押すと3つの爪が持ち上がり、それぞれが対応するスクランブラーの歯車とかみ合い、回転させようとする。しかし1番目のスクランブラーは問題なく回転するが、2番目と3番目のスクランブラーでは爪と歯車がかみ合わず、普段は回転しない。ではどんな時にそれらが回転するか。ここでノッチが登場する。

・ノッチ
英: Notch
スクランブラーの縁に刻まれた切欠き。普段は、2番目と3番目のスクランブラーに対応する爪は前のスクランブラーの縁に阻まれ、歯車とかみ合わない。しかし爪がノッチにかみ合うと、歯車ともかみ合ってスクランブラーを回転させることができる。

・ターンオーバー
英: Turnover 独: Übertragskerbe
ノッチのおかげで次のスクランブラーが回転しようとしているときに、表示窓に見える文字(数字)。例えばEnigma IのスクランブラーIのターンオーバーはQ(17)である。つまり、表示窓に見える文字がQ(17)からR(18)に切り替わる時、隣のスクランブラーが回転する。
シミュレーターを作るために重要な要素の1つ。

・リング
英: Ring 独: Ring
スクランブラーの内部配線をずらすことができる仕組み。具体的にどう作用するかは後述。
実装が少々手間なので、まずはリングぬきのエニグマ作成を考え、それからリングを実装することにする。
リング設定もまた、エニグマ暗号の鍵の1つである。

リフレクター(UKW)

英: Reflector 独: Umkehrwalze
通常、リフレクターの配線は作った時点で決まっており、使用者が変更することが出来ない。ただし変更可能なタイプも存在する(今回は扱わない)。
これは使用中に動かないので、換字表を増やす役には立たない。しかし、リフレクターがあることによって、プレーンテキスト文字Aが暗号文字Bに変換される場合、プレーンテキストBは暗号文字Aに変換されるという性質が得られる(反転性)。したがって、暗号文を再度暗号化するとプレーンテキストが得られるので、機械構造が単純になる(復号を行うためにわざわざ別の機構を用意する必要がない)。
スクランブラー同様にこれも何種類かあるが、リフレクターはスクランブラーのようにこれらを切り替えて使うという運用はされず、基本的に固定だったらしい。エニグマ暗号の鍵にリフレクター設定が含まれるという記述は見かけない。

ランプボード

英: Lamp board 独:Lampenfeld
キーを押して回路が形成されると、ランプボードの1つのランプが点灯する。これが暗号化された文字を表す。Aのキーを押すとにBのランプが点灯する場合、AはBに暗号化されることを意味する。

Uhr

Enigma IはUhr(ドイツ語で時計を意味する)というものを設定できる。エニグマ本体に付属して使う小さなデバイスであり、プラグボードに接続して使うらしい。よく分からないのでここでは取り扱わない。
詳しくはここを参照。
https://www.cryptomuseum.com/crypto/enigma/uhr/index.htm

配線の表記方法について

暗号化を行うためには、スクランブラーとリフレクターの配線を記述する必要がある。どのように表記するかは要注意点である。ここをはっきりさせないと大変なことになる。というのも、やり方が少なくとも2通りあるからだ。これを実際のエニグマ暗号機Enigma IのスクランブラーIを例にとって観察してみる。
なお、2つの表記方法に定まった呼称はない。

方法1(仮称:異字接続法)

上段にABCDE……Zと書き、それぞれの文字の直下に、換字表によって変換される文字を書く方法である。

ABCDEFGHIJKLMNOPQRSTUVWXYZ
EKMFLGDQVZNTOWYHXUSPAIBRCJ

この表記方法を詳しく見る。まずは縦にABCDE……Zと2列書く(実物のスクランブラーにそう書かれているわけではない)。右列をキーボード側、左列をリフレクター側と見なす。そして右列のAと左列Eを、右列のBと左列のKを、右列のCと左列のMを、……、右列のZと左列のJを結ぶ。

そのような配線の一部を図1に示す。

図1. 方法1における配線A-E、N-W
図の右側がキーボード側、左側がリフレクター側を表す

方法2(仮称:同字接続法)

上段にABCDE……Zと書き、上下の同じ文字同士がワイヤーで接続されるように下段に文字を書き並べる方法である。

ABCDEFGHIJKLMNOPQRSTUVWXYZ
UWYGADFPVZBECKMTHXSLRINQOJ

この表記方法を詳しく見る。2つの列を用意し、右の列にABCDE……Zと縦に書くまでは方法1と同じである。しかしここからが異なる。左の列に上述のUWYGA……Jを順に書き込む。そして右列のAと左列のAを、右列のBと左列のBを、右列のCと左列のCを、……右列のZと左列のZを結ぶ。つまり、配線表記の上段の文字が、下段の同じ文字につながるようにワイヤーが配線されている。

そのような配線の一部を図2に示す。

図2. 方法2における配線A-A、N-N
図の右側がキーボード側、左側がリフレクター側を表す

2つの方法には良し悪しはあっても優劣はない(多分)。一般的なのは1番目の表記であるが(多分)、ここでは2番目の方法を採用することにする。その理由は単に筆者がこちらの方法で作り始めたからであり、両方のやり方を比較検討したわけではない。

暗号化の過程を観察する

単純にするためにスクランブラーは1個だけにする。初期状態として以下の状況を考える。

・プラグボード: AE, BM, CV, HZ, KX, OS, RTの7組を交換する。
・スクランブラー: Enigma IのスクランブラーIを使用。初期ポジション設定はA。リング設定はいじらない(初期設定のAのまま使用する)。
・リフレクター: Enigma IのUKW-Aを使用。

これを図3に示す。実物のエニグマ暗号機に倣って、右側が出入力側、左側がリフレクター側とする。
スクランブラー右列の一番上(位置1)にA(01)がある。以下、この状態を「スクランブラーのポジションはA(01)である」というように表現する。

図3. 簡易版エニグマの初期設定の例
スクランブラーのポジションはA(01)である

Aのキーを押す。すると、スクランブラーが1ステップ回転する。回転の方向は、図の上方向である(上からはみ出た文字は最下段から現れる)。スクランブラーのポジションはA(01)からB(02)に切り替わる。
その後に信号が流れる。ここが要注意点である。信号はスクランブラーが回転した後に流れる。当初、筆者はこのことに気づかず、誤って信号が流れた後にスクランブラーが回転するものとして作っていたため、おかしなことになった。

信号が流れ、ランプボードのMが点灯する。すなわちAはMに変換される。その過程を図4に示すと次のようになる。

図4. 信号の流れ
スクランブラーのポジションがB(02)になっている

過程を詳述すると次のようになる。
1. 入出力のAに印をつける。
2. 入出力のAから左にまっすぐ直線を引く。プラグボード右側のAに突き当たる。
3. プラグボード右側のAと左側のAを線で結ぶ。
4. プラグボード左側のAから左にまっすぐ直線を引く。スクランブラー右側のFに突き当たる。
5. スクランブラー右側のFと左側のFを線で結ぶ。
6. スクランブラー左側のFから左にまっすぐ直線を引く。リフレクター右側のFに突き当たる。
7. リフレクター右側のFに対応する左側の文字はLである。よってリフレクター右側のFとLを線で結ぶ。
8. リフレクター右側のLから右にまっすぐ直線を引く。スクランブラー左側のCに突き当たる。
9. スクランブラー左側のCと右側のCを線で結ぶ。
10. スクランブラー右側のCから右にまっすぐ直線を引く。プラグボード左側のMに突き当たる。
11. プラグボード左側のMと右側のMを線で結ぶ。
12. プラグボード右側のMから右にまっすぐ直線を引く。入出力のMに突き当たる。これに印をつける。

シミュレーターの動作原理

実際に制作する前に、まず本シミュレーターがいかなる考えにおいて文字の変換を行っているか、その概念を示す。1~26で表される「信号が流れる位置」がプラグボードやスクランブラーなどで次々変化していき、最終的に信号が流れている位置に対応する文字が、暗号化された文字であるとする方式である。この流れの一部を次の図5に示す。

これ以降の配線図は本シミュレーターでのやり方を基準とするため、入力側を左、出力側を右として表現することに注意。

図5. 信号の流れ
()内の数字は位置を示すが、スクランブラーでは「内部的な位置」を表していることに注意

Aを入力する。Aは1番目の文字なので、信号は位置1を流れる。プラグボードではAとJが交換されるので、信号の流れる位置は10に変わる。そしてスクランブラー1に入るのだが、ここで注意することがある。図の状況では、位置10にあるスクランブラー1の入力側の文字はJ(10)ではなくK(11)である。これは、スクランブラー1のポジションがB(2)である、つまりポジションA(1)から2-1=1ステップだけ回転しているため、このような差が生じる。
これを補正するため、スクランブラーの「内部的な位置」を考える。スクランブラーがnステップ回転しているとき、位置pを流れる信号はスクランブラー入力側のp+n番目の「内部的な位置」に入る。そして出力側で信号の「内部的な位置」がqである時、スクランブラーを出た信号は位置q-nを流れることになる。
上の図で言えば、位置10を流れる信号はスクランブラー入力側の10+1=11番目の位置に入る。対応する文字はKである。よって出力側のKの位置に信号が移る。その「内部的な位置」は14である。これにスクランブラーの回転の影響を除くと、14-1=13番目の位置に信号が出力されることになる。以下同様に信号が流れていく。

エニグマ(リングぬき)を作る

スプレッドシート状にエニグマシミュレーターを作っていく。完成すると図6のようになる。
画像下部のフィールドでは文字の暗号化を行っている。1つの行ごとに1文字を扱い、シートの左側から右側に向かって暗号化が進行していく。

図6. 我流エニグマシミュレーター

文章入力部分

図7. 文章入力部分
A1 暗号化する前の文章
A2 文章の文字数
A3 暗号化された後の文章

まずA1のセルは暗号化(あるいは復号)したい文章を入力する場所とする。何に使うというわけではないが、A2に一応文章の文字数を取得する。
暗号化された文章を表示する場所をA3と決めておく。

具体的に何を書くか
・A1
何も書かなくてもよく、適当に「AAAAAA……」など好きな文字列を書いておいてもよい。自由スペースと分かりやすいよう黄色を塗っておく。

・A2
「=len(A1)」と書く。lenは文字列の長さを返す関数である。このセルは一応赤を塗っておく。

・A3
本シミュレーターでは「=textjoin(,,C17:C1000)」と書かれている。後述するが、C列は暗号化された文字が1文字ずつ並ぶ予定であり、これらを結合して暗号化の結果の文字列を得る。textjoinは複数のテキストを結合し、指定した区切り文字(今回は何も指定しない)をそれぞれのテキスト間に挿入する関数である。テキストの結合ができれば他の方法でもよい。このセルは一応赤を塗っておく。

設定部分

図8. 設定部分
D6:F6 初期ポジション設定
D7: F7 リング設定
D8:F8 ターンオーバー設定
D9:F12 対応する文字の数値化
I5 スクランブラー回転方向設定

設定を用意する。3つのスクランブラーに対して、ポジション初期設定、(今は扱わないが)リング設定、ターンオーバー、およびそれぞれの番号を書き入れる欄を作る。黄色で示したセルは自分で自由に書き込めるところで、赤で示したセルは他を参照するので触ってはいけない箇所である。ターンオーバーはスクランブラー固有の要素であり、自由に設定できるものではないが、便宜上ここに置いておく。
また、スクランブラーの回転方向も変えられるようにする。実物のエニグマ暗号機ではスクランブラーの回転方向は変えられないが、一応逆回転もできるようにしておく。

具体的に何を書くか
・「ポジション」
自由スペース。今は適当に「A」と書いておく。黄色に塗っておく。

・「リング」
これも適当に「A」と書いてセルを黄色に塗っておく。

・「ターンオーバー」
例として上図D8セルには「=AS32」と書かれている。これはスクランブラー設定部分のターンオーバーの情報を参照している。今はまだそこに何もないので、仮に「Q」などの好きな文字を書いておいてもよい。この行は赤に塗っておく。

・「ポジションNo.」
例として上図D9セルには「=code(D6)-64」と書かれている。codeは指定した文字列の先頭文字に対応するユニコード表の数値を返す関数である。code()の中には、自身の3つ上のセルを指定する。このシミュレーターでは、Aを1、Bを2、……というように文字を数字に変換する時、「文字のASCIIコードを取得してそこから64を引く」という方法を取る(多分もっとスマートな方法があるかもしれないが知らない)。この行は赤に塗っておく。

・「リングNo.」
同上。

・「TO No.」
ターンオーバーNo.の意。同上。

・「回転方向」
「1」と書く。逆回転するときは「-1」と書く。

スクランブラー設定部分

スクランブラー設定部分を図9に示す。ついでにプラグボードとリフレクターの設定も同様の形式で行っている。
なお、このシミュレーターでは左から右へと処理が進行していく関係上、左側の列がキーボード側、右側の列がリフレクター側を示している。

図9. プラグボード、スクランブラー、リフレクターの配線設定部分
「TO.」は各スクランブラーのターンオーバーを示す

黄色で示したセルについて、基本的に記入内容は自由である。ただし、正しく配線を記述しないとエラーが生じる。

このタイミングで設定部分の「ターンオーバー」にターンオーバーの情報を渡してもよい。

入出力部分

図10. 入出力部分

「No.」は、文章の何文字目に注目しているかを表す。例えば「No.」が1の行では、文章の1文字目に注目し、その暗号化を行う。
「原字」は、「No.」の値がnの場合、暗号化前の文章からn文字目を取得したものである。
「暗字」は、暗号化した結果得られた文字を示す。シートのずっと右側で得られた結果を、便宜上原字の隣に持ってきている。これらを結合し、結果セル(ここではA3)に書き込む予定である。

具体的に何を書くか
・「No.」
上から1,2,3,4,……と書き込んでいけばよい。

・「原字」
セルB17を例にとると「=mid($A$1,A17,1)」のように書く。midは指定した文字列から一部を取り出す関数である。

・「暗字」
セルC17を例にとると「=iferror(AK17,"")」のように書く。これは処理フィールドの右端(AK17)で暗号化された文字が得られるので、そこを参照している。ただし、文章の文字数を超えるNo.の行では原字は空欄になり、したがってこれを変換しようとするとエラーが生じる。その場合は何も書かないようにする。iferrorはエラーが生じた場合の処理を記述するために用いている。今はそこに何も書かれていないので、上のように書いても何も表示されない。

プラグボード部分

図11. プラグボード部分

「PB入力」では入力された文字を数値に変換する。Aが入力されたら1というようにである。この列がシミュレーション上特に役に立っていないことに今(執筆時点)気づいた。
「PB出力/S1入力」はプラグボードの出力であり、かつスクランブラー1の入力である。スクランブラー設定からプラグボード配線の右側の列を見て、その中で原字と一致する文字が書かれている位置番号を取得する。

具体的に何を書くか
・「PB入力」
上図セルD17を例にとると「=code(B17)-64」のように書く。

・「PB出力/S1入力」
セルE17を例にとると「=match(B17,$AQ$6:$AQ$31,false)」のように書く。matchは指定した値と一致する範囲内のアイテムの相対的な位置を返す関数である。

スクランブラー1部分

ここがシミュレーターの核である。

図12. スクランブラー1部分

「PB出力/S1入力」は上述の通りである。
「S1位置」は、スクランブラーのポジションを示す。ここで、キーを押してスクランブラーが回転した後に信号が流れるということを思い出してほしい。つまり文章の1文字目は、スクランブラーの初期設定ではなく、そこから1ステップ進んだ設定で暗号化されることになる。
「S1入力(内部)」は信号が入るスクランブラー1の「内部的な位置」である。おさらいすると、スクランブラーがnステップ回転しているとき、位置pを流れる信号はスクランブラー内部のp+n番目の位置に入る。
「文字」は、スクランブラー内部のp+n番目の位置に対応する文字である。
「S1出力(内部)」は信号が出るスクランブラー1の「内部的な位置」である。
「S1出力/S2入力」は、スクランブラー1を出た信号が流れる絶対的位置である。出力の「内部的な位置」がqであるとすると、絶対的位置はq-nで求められる。これがスクランブラー2に入る。

具体的に何を書くか
・「S1位置」
先頭の行(上図F17のセル)は設定を参照して「=mod($D$9+$I$5-1,26)+1」と書く。ここで$D$9はスクランブラー1の初期ポジションの番号、$I$5は回転方向(1または-1)を表す。ただしこのままでは計算結果が1~26の範囲からはみ出ることがある。そのため少々手間がかかるが、剰余計算を行うmod関数を使っている。式の意味は、計算結果から1を引き、26で割った余りを求めて値を0~25の範囲に収め、それから1を加えて結果を1~26の範囲に収めるということである。多分もっとスマートな方法があると思うが知らない。
2行目以降は1つ前の行を参照する。例えばF18のセルの場合は「=mod(F17+$I$5-1,26)+1」と書く。

・「S1入力(内部)」
例としてG17のセルでは「=mod(E17+(F17-1)-1,26)+1」と書く。信号の絶対的位置(E17)にスクランブラーの回転ステップ数(F17-1)を加えるのだが、このままだと1~26の範囲をはみ出す場合があるので、さらに1を引いて26で割って1を足すという手間のかかる計算をしている。「=mod(E17+F17-2,26)+1」としても結果は同じ。

・「文字」
例としてH17のセルでは「=char(G17+64)」と書く。スクランブラー1の内部的な位置を対応する文字に変換している。

・「S1出力(内部)」
例としてI17のセルでは「=match(H17,$AS$6:$AS$31,false)」と書く。スクランブラー設定部分のスクランブラー1の右列から、「文字」に書かれた文字を探し、その位置番号を取得している。

・「S1出力/S2入力」
例としてJ17のセルでは「=mod(I17-(F17-1)-1,26)+1」と書く。スクランブラー1の「内部的な位置」(I17)から回転ステップ数(F17-1)を引いているのだが、例によってさらに1を引いて26で割って1を足すという計算をして値を1~26の範囲に収めている。「=mod(I17-F17,26)+1」としても結果は同じ。

スクランブラー2部分

図13. スクランブラー2部分

各列の意味や式はスクランブラー1の場合と同様である。ただし、スクランブラー2の位置を示す「S2位置」は、スクランブラー1の位置がターンオーバーと一致した次のステップでのみ変化する点には注意。

具体的に何を書くか
・「S2位置」
先頭の行(上図K17のセル)は「=if($D$9=$D$11,mod($E$9+$I$5-1,26)+1,$E$9)」と書く。ここで$D$9はスクランブラー1の初期ポジションの番号、$D$11はターンオーバーの番号、$E$9はスクランブラー2の初期ポジションの番号、$I$5は回転方向(1または-1)を表す。スクランブラー1の位置がターンオーバーと一致した場合のみ回転を行うようにしている。これも例によって「計算結果から1を引いて26で割った余りを求めて1を足す」という操作をして結果を1~26の範囲に収めている。
2行目以降は1つ前の行を参照する。例えばK18のセルの場合は「=if(F17=$D$11,mod(K17+$I$5-1,26)+1,K17)」と書く。

・「S2入力(内部)」
例としてL17のセルでは「=mod(J17+(K17-1)-1,26)+1」と書く。スクランブラー1の場合と同様。

・「文字」
例としてM17のセルでは「=char(L17+64)」と書く。スクランブラー1の場合と同様。

・「S2出力(内部)」
例としてN17のセルでは「=match(M17,$AU$6:$AU$31,false)」と書く。スクランブラー1の場合と同様。

・「S2出力/S3入力」
例としてO17のセルでは「=mod(N17-(K17-1)-1,26)+1」と書く。スクランブラー1の場合と同様。

スクランブラー3部分

図14. スクランブラー3部分

各列の意味や式はスクランブラー1, 2の場合と同様である。ただし、スクランブラー3の位置を示す「S3位置」は、スクランブラー1, 2の位置がそれぞれのターンオーバーと一致した次のステップでのみ変化する点には注意。

具体的に何を書くか
・「S3位置」
先頭の行(上図P17のセル)は「=if(and($D$9=$D$11,$E$9=$E$11),mod($F$9+$I$5-1,26)+1,$F$9)」と書く。
2行目以降は1つ前の行を参照する。例えばP18のセルの場合は「=if(and(F17=$D$11,K17=$E$11),mod(P17+$I$5-1,26)+1,P17)」と書く。

・「S3入力(内部)」
例としてQ17のセルでは「=mod(O17+(P17-1)-1,26)+1」と書く。スクランブラー1,2の場合と同様。

・「文字」
例としてR17のセルでは「=char(Q17+64)」と書く。スクランブラー1,2の場合と同様。

・「S3出力(内部)」
例としてS17のセルでは「=match(R17,$AW$6:$AW$31,false)」と書く。スクランブラー1,2の場合と同様。

・「S3出力/R入力」
例としてT17のセルでは「=mod(S17-(P17-1)-1,26)+1」と書く。スクランブラー1,2の場合と同様。

リフレクター部分

図15. リフレクター部分

各列の意味や式はスクランブラーと同様である。

具体的に何を書くか
・「文字」
例としてU17のセルでは「=char(T17+64)」と書く。リフレクターへの入力信号の位置を、対応する文字に変換している。

・「R出力/S3入力」
例としてV17のセルでは「=match(U17,$AY$6:$AY$31,false)」と書く。「文字」に記された文字をスクランブラー設定部分のリフレクターの右列から探し、その位置を取得している。

スクランブラー3, 2, 1部分

図16. スクランブラー3,2,1部分

各列の意味や式は信号がリフレクターに向かっている場合と同様である。ただし、スクランブラー設定部分のスクランブラー1,2,3の右列が入力、左列が出力になることに注意。

具体的に何を書くか
・「S3入力(内部)」
例としてW17のセルでは「=mod(V17+(P17-1)-1,26)+1」と書く。ここでV17はスクランブラー3に入る信号の絶対位置、P17はスクランブラー3のポジションである。

・「文字」
例としてX17のセルでは「=index($AW$6:$AW$31,W17,1)」と書く。$AW$6:$AW$31はスクランブラー設定部分のスクランブラー3の右列、W17はスクランブラー3に入る信号の「内部的な位置」である。

・「S3出力(内部)」
例としてY17のセルでは「=code(X17)-64」と書く。左のセルで得た文字を数値化したものが、スクランブラー3の出力の「内部的な位置」である。

・「S3出力/S2入力」
例としてZ17のセルでは「=mod(Y17-(P17-1)-1,26)+1」と書く。スクランブラー3から出る信号の絶対的位置である。

以下省略。

プラグボード部分・出力部分

図17. プラグボード部分、出力部分

具体的に何を書くか
・「PB入力文字」
例としてAI17のセルでは「=index($AQ$6:$AQ$31,AH17,1)」と書く。

・「PB出力」
例としてAJ17のセルでは「=match(AI17,$AP$6:$AP$31,false)」と書く。

・「暗字」
例としてAK17のセルでは「=char(AJ17+64)」と書く。

この「暗字」の出力結果をシート左方のC列に持ってきている。

スクランブラーなど置き場

シミュレーター部分は一段落したので、ついでにスクランブラーとリフレクターの置き場を作る。ここにEnigma Iなどのスクランブラーやリフレクターを用意し、必要になったらここからコピー・ペーストして使用することができるようにする。
新しいワークシートを開き、図18のように準備する。

図18. スクランブラー、リフレクターの置き場

リング設定を観察する

リングを実装する前に、その挙動を観察する。観察するスクランブラーは引き続きEnigma IのスクランブラーIである。まず、ポジション設定をA、リング設定をAにしてみる。次にリング設定を1つずらし、Bにする。すると、ワイヤー配線が1つずれる。これらを図19に示す。配線がずれた結果の配線表記はまだ分からない。

図19. リング設定概念
リング設定がBのときのワイヤー配線表記はまだ不明

?だらけの右列に、同じ文字がつながるように正しい文字を埋めていく。上図の?(6)は左列のB(2)とつながっているから、ここにはB(6)と書く。?(5)は左列のH(8)とつながっているから、H(5)である。同様にして右列に文字を書き込むと次の図20のようになる。

図20. リング設定Bのときのワイヤー配線表記を確定させる

リング設定Bにおけるワイヤー配線表記は次のようになる。

ABCDEFGHIJKLMNOPQRSTUVWXYZ
KVXZHBEFQWACFDLNUIYTMSJORP

以上の操作で何をやっているのかを観察する。リング設定をAからBに1つずらした。すると、左列(キーボード側)の文字はそのままで、右列(リフレクター側)の文字だけ1つ下に移動し(下からはみ出た文字は最上段から現れ)、さらに1つ後の文字(Zの後は再びA, B, C,…と続く)に置き換わったということが分かる。

これを一般化する。リング設定をAからnだけずらすと、左列の文字はそのまま、右列の文字はnだけ下に移動し、その上でアルファベット順でnだけ後の文字に置き換わる。

リング設定の変更はスクランブラー内部で行われるので、スクランブラーの縁についているノッチは動かず、したがってターンオーバーは変化しない。

リング設定はワイヤーの配線をずらすだけなので、適当なポジション設定にすると、結局ワイヤーの位置は元に戻る。これで何が変わるのかというと、ターンオーバーの位置である。例えば、「ポジション設定A、リング設定A」と、「ポジション設定B、リング設定B」の場合のワイヤー配線は一致するが、ターンオーバーのQ(17)は1つずれている。これを図21に示す。

図21. 「ポジション設定A、リング設定A」のときと「ポジション設定B、リング設定B」のときのワイヤー配線は一致する
しかし黄色で示したターンオーバーQ(17)の位置が変わっている

エニグマにリングを実装する

一度リング設定を決めてしまえば、それ以降のスクランブラー配線は変わらない。そのため、作業フィールドの編集や拡張は行わず、「スクランブラー設定部分」の方を編集する。現在の「スクランブラー設定部分」の右隣りにコピーを用意する。そして左側を「リング補正込みスクランブラー設定部分」に、右側を「デフォルトのスクランブラー設定部分」とする。そして「リング補正込みスクランブラー設定部分」を編集する。これを図22に示す。

図22. スクランブラー設定部分をリング補正込み(左)とデフォルト(右)に分ける
左側の黄色だったセルは赤に塗り直している

プラグボード、リフレクター、およびスクランブラーのターンオーバーは右側の対応するセルの値をそのまま参照すればいい。問題はワイヤー配線である。たとえば、AS6のセルには次のように書く。本シミュレーター中最も複雑な式である。
「=char(mod(code(index($BE$6:$BE$31,mod($AO6-($D$10-1)-1,26)+1,1))+($D$10-1)-65,26)+65)」

とにかく複雑なので、何をやっているのかを順に見ていく。
・「$D$10-1」
スクランブラー1のリング設定の数値($D$10)を取得し、そこから1を引いて、リング設定Aの場合からのずれを求める。これをnとする。
・「mod($AO6-($D$10-1)-1,26)+1」
現在注目している位置(AS6)の番号($AO6)からnを引き、その結果を1~26の範囲に収める。
・「index($BE$6:$BE$31, mod($AO6-($D$10-1)-1,26)+1,1)」
リング設定Aのときのスクランブラー1のワイヤー配線($BE$6:$BE$31)を参照し、現在注目している位置からnだけ上の位置にある文字を取得する。
・「code(index($BE$6:$BE$31, mod($AO6-($D$10-1)-1,26)+1,1))」
取得した文字をASCIIコード変換し、65~90の数値に変換する。
・「mod(code(index($BE$6:$BE$31,mod($AO6-($D$10-1)-1,26)+1,1))+($D$10-1)-65,26)+65」
得られた数値にnを加え、その結果を65~90の範囲に収める。
・「char(mod(code(index($BE$6:$BE$31,mod($AO6-($D$10-1)-1,26)+1,1))+($D$10-1)-65,26)+65)」
得られた数値をASCIIコードにより文字に変換する。これが結果の文字である。

これでエニグマ暗号機のシミュレーターが完成した。

比較

オンラインシミュレーターと自作エニグマシミュレーターを比較する。

オンラインシミュレーターの設定
使用シミュレーター: 「DenCode」のエニグマ暗号機シミュレーター(こちら)を使用。
暗号機: Enigma I
ウィール: UKW-B III V II(右からスクランブラー1,2,3,リフレクターである)
リング設定: W(23) X(24) R(18)(右からスクランブラー1,2,3である)
ポジション設定: S(19) L(12) D(04)(右からスクランブラー1,2,3である)
プラグボード設定: AT BL DF GJ HM NW OP QY RZ VX
Uhr: 00

自作シミュレーターの設定: 図23の通り。

図23. 設定

入力文AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA(100個のA)
オンラインシミュレーター出力文
HSLCSJRWXNCUNNZEZPBLZIEZPVYHTVTNJWWGYHNDGXEWQVXUHZUKVTGLHOOXEBGKQBYWLOMICRJMKCFUGXKUWXNTMKKIYHRYDXIR
自作シミュレーター出力文
HSLCSJRWXNCUNNZEZPBLZIEZPVYHTVTNJWWGYHNDGXEWQVXUHZUKVTGLHOOXEBGKQBYWLOMICRJMKCFUGXKUWXNTMKKIYHRYDXIR

両者は一致したので、自作シミュレーターの正確さにはある程度自信を持ってもよさそうである。

まとめ

エニグマ暗号機がどのように動作するかを理解したら、以上述べた制作方法はきれいさっぱり忘れてしまってもよい。もっと簡潔な実装方法があるはずである。

ただし以下のポイントは、制作にあたって筆者がつまづいたところである。どのような方法・環境でシミュレーターを作るにせよ覚えておくべきことなので、ここに書き記しておく。

・キーを押してスクランブラーが回転した後に信号が流れること。
・ワイヤーの配線を表記する方法は2通りあること。参考にしている情報源がどちらの方法を使っているのか注意。
・実物のエニグマ暗号機においてスクランブラー位置は右から1,2,3と数えること。参考にしているシミュレーターにおけるスクランブラー配列順に注意。
・ターンオーバーは地味だがワイヤー配線に劣らず大事な要素であること。

参考文献・資料

How did the Enigma Machine work?
エニグマ暗号機がどのようにして動作するかを詳しく見る動画。
Enigma Cipher Machine - Crypto Museum
エニグマ暗号機について解説しているサイト。
エニグマ暗号機シミュレーター Online - DenCode
様々なモデルのエニグマ暗号機のシミュレーター。


この記事が気に入ったらサポートをしてみませんか?