シーザー暗号やヴィジュネル暗号は、人間が手作業で暗号化・復号する「手動暗号」であった。20世紀に入ると、通信量の増大に伴い、より高速かつ安全な暗号化手段が求められるようになる。その要求に応えたのが、電気機械式暗号機エニグマ(Enigma)である。
エニグマの本質は「1文字打つたびに換字表が自動で切り替わる」ことにある。単一換字式暗号では同じ文字は常に同じ暗号文字になるため、頻度分析で解読できた。エニグマはローター(回転子)の回転により、キーを押すたびに内部の換字規則が変化する。同じ「A」を2回打っても、1回目は「G」、2回目は「T」のように異なる結果になるのである。
エニグマは、ドイツの電気技術者アルトゥール・シェルビウス(Arthur Scherbius, 1878〜1929)によって発明された。シェルビウスは1918年2月23日に回転式暗号機の特許を出願し、1923年に「エニグマ」(ギリシャ語で「謎」の意)の名で商用市場に売り出した。
1926年にドイツ海軍が改良版を採用し、続いて陸軍・空軍も導入したことで、エニグマは第二次世界大戦におけるドイツ軍の主力暗号システムとなった。戦争中に推定約40,000台が製造されたとされる。シェルビウス自身は1929年に馬車の事故で亡くなり、自らの発明が戦争の帰趨を左右する存在になるのを見届けることはなかった。
構造
エニグマの外見はタイプライターに似た木製の箱型装置である。操作者がキーボードで文字を打つと、内部で電気信号が複雑な経路を通り、ランプボード上の別の文字が点灯する。この点灯した文字が暗号文である。
内部は大きく4つの部品で構成される。信号は「プラグボード → ローター群 → リフレクター → ローター群(逆方向) → プラグボード」の順に通過する。
ローター(Walzen)―エニグマの心臓部
各ローターは直径約10cmの円盤で、左右にそれぞれ26個の電気接点を持つ。内部の配線により、例えば「A」の接点に入った信号が「E」の接点から出るといった具合に、1つの換字を行う。つまりローター1個は換字式暗号1つに相当する。
軍用エニグマでは5種類のローターから3つを選んで並べた。最も重要な特徴は、1文字暗号化するたびに右端のローターが1ステップ回転することである。回転するとすべての配線がずれるため、次の文字では全く異なる換字が行われる。右端のローターが1周すると中央のローターが1ステップ進み、中央のローターが1周すると左端のローターが進む。この仕組みは自動車の走行距離計(オドメーター)と同じである。
リフレクター(Umkehrwalze)―折り返しの鏡
ローター群の左端に固定された反射板である。信号がローターを左端まで通過すると、リフレクターが信号を折り返す。折り返された信号はローター群を逆方向に通り、異なる経路で戻ってくる。
リフレクターの最大の利点は、暗号化と復号が全く同じ操作になることである。受信者は同じ設定のエニグマに暗号文を打ち込むだけで、平文が点灯する。
プラグボード(Steckerbrett)―文字の入れ替え
キーボードとローター群の間にある配線盤で、ケーブルを差し込んで任意の2文字を交換する。例えば「A」と「G」をケーブルで接続すると、「A」を打ったときに「G」としてローターに入り、ローターから「A」として出てきた信号は「G」のランプを点灯させる。通常10本のケーブルで20文字をペアにして入れ替えた。
図解
エニグマの内部で電気信号がどのように流れるかを示す。ここでは簡略化のため6文字(A〜F)のローターで表現している。
図のポイントを整理しよう。信号は右(キーボード側)から左(リフレクター側)へ進み、リフレクターで折り返されて左から右へ戻ってくる。往路と復路では同じローターを通るが、方向が逆なので異なる配線を通る。これにより、入力「A」と出力「B」のように、複雑な変換が実現される。
上の図は「1文字目」の状態である。2文字目を打つ前に右ローターが1ステップ回転するため、ローターR内部の配線が丸ごとずれる。すると信号の経路が全く変わり、同じ「A」を打っても異なる文字が出力される。3つのローターの組み合わせにより、26^3 = 17{,}576文字を打つまでローターの状態は一巡しない。
リフレクターは信号を必ず別の接点に折り返す構造になっている。つまり「A」を入力して「A」が出力されることは絶対にない。一見すると安全性を高める特徴に思えるが、実際には暗号解読者にとって強力な手がかりとなった。推測した平文(クリブ)を暗号文に当てはめる際、同じ位置に同じ文字がある場合はその配置を即座に排除できるためである。
暗号化の手順
エニグマの暗号化を、1文字ずつ追ってみよう。
ステップ1:ローターが回転する。キーを押すと、まず右端のローターが1ステップ回転する。この回転は暗号化の前に行われる。右ローターが特定位置(ノッチ)に達すると、中央のローターも1ステップ進む。
ステップ2:プラグボードを通過する。入力された文字がプラグボードで別の文字に交換される(ケーブルが接続されていない文字はそのまま通過する)。
ステップ3:ローター群を右から左へ通過する。信号は右ローター → 中ローター → 左ローターの順に通過する。各ローターの内部配線に従って、文字が変換されていく。
ステップ4:リフレクターで折り返す。信号がリフレクターで別の文字に変換され、ローター群に折り返される。
ステップ5:ローター群を左から右へ通過する。信号は左ローター → 中ローター → 右ローターの順に、往路とは逆方向に通過する。逆方向では異なる換字が行われる。
ステップ6:プラグボードを再度通過する。最後にプラグボードを通り、ランプボード上の文字が点灯する。この文字が暗号文となる。
ローターI(右)、II(中)、III(左)を装着し、初期位置をすべてA、プラグボードなしの設定で「HELLOWORLD」を暗号化すると、暗号文は「MFNCZBBFZM」となる。
平文「HELLO」の中で「L」が2回連続するが、暗号文では「NC」と異なる2文字になっている。これはローターが1文字ごとに回転し、換字表が切り替わっているためである。
復号は同じ設定で暗号文を入力するだけでよい。「MFNCZBBFZM」を入力すると「HELLOWORLD」が得られる。
鍵空間
エニグマの安全性は、設定可能なパラメータの組み合わせが膨大であることに依存していた。軍用エニグマ(Wehrmacht Enigma、ローター5種・ケーブル10本)の鍵空間を計算する。
| 設定項目 | 計算 | 通り数 |
|---|---|---|
| ローターの選択と配置 | 5 \times 4 \times 3 | 60 |
| ローターの初期位置 | 26^3 | 17,576 |
| リング設定 | 26^2 | 676 |
| プラグボード(10本) | \dfrac{26!}{6! \cdot 10! \cdot 2^{10}} | 約1,507億 |
約1,070垓(1の後ろに0が23個)通りの設定が存在し、これは現代の暗号で約77ビットの鍵に相当する。当時の技術では総当たり攻撃は完全に不可能であり、ドイツ軍はエニグマを解読不能と確信していた。
解読の歴史
ポーランド暗号局の功績(1932年)
エニグマ解読の物語は、イギリスのブレッチリー・パークよりも7年も早く、ポーランドで始まった。
ポーランド参謀本部の暗号局(Biuro Szyfrów)に所属する数学者マリアン・レイェフスキ(Marian Rejewski, 1905〜1980)は、1932年12月、フランス軍情報部経由で入手したドイツ人スパイ(ハンス=ティロ・シュミット、暗号名「アシェ」)の資料を手がかりに、置換群の理論を応用してエニグマの内部配線を数学的に逆算することに成功した。
レイェフスキと同僚のイェジ・ルジツキ(Jerzy Różycki, 1909〜1942)、ヘンリク・ジガルスキ(Henryk Zygalski, 1907〜1978)は、その後7年間にわたりドイツ軍のエニグマ通信を解読し続けた。彼らは「ボンバ」(bomba)と呼ばれる電気機械式解読装置を開発し、日替わりの鍵設定を約2時間で特定できるようにした。
ブレッチリー・パークとアラン・チューリング(1939年〜)
1938年12月にドイツがローターを3種類から5種類に増やしたことで、ポーランドの手法は限界に達した。1939年7月25日、ドイツの侵攻が迫る中、ポーランドはエニグマの複製機と解読技術をイギリス・フランスに引き渡した。
イギリスのブレッチリー・パーク(政府暗号学校)では、数学者アラン・チューリング(Alan Turing, 1912〜1954)がポーランドの成果を発展させた。チューリングは1940年に「ボンベ」(Bombe)と呼ばれる新型の解読装置を設計した。ボンベは「クリブ」(推測される平文の断片)と「ある文字は自分自身に暗号化されない」という性質を利用して、ローター設定を効率的に絞り込んだ。戦争中に約200台が製造された。
解読で得られた情報は「ウルトラ」(Ultra)と呼ばれ、大西洋の戦い(Battle of the Atlantic)をはじめとする多くの作戦に決定的な影響を与えた。
エニグマ解読の事実は戦後も長く機密扱いとされ、1973年にようやく公開された。ポーランドの暗号解読者たちの功績が広く知られるようになったのはさらに後のことである。2014年、IEEE(米国電気電子学会)はレイェフスキ、ルジツキ、ジガルスキの3名にマイルストーン賞を授与し、彼らの業績を正式に称えた。
安全性と弱点
エニグマの鍵空間は約10^{23}通りと膨大であったが、いくつかの弱点が解読の糸口となった。
設計上の弱点として、前述のとおり「ある文字が自分自身に暗号化されない」ことが挙げられる。これにより推測平文と暗号文の照合で不一致の位置を即座に排除でき、膨大な候補の絞り込みが可能になった。
運用上のミスも深刻であった。天気予報の定型文や「Heil Hitler」のような決まり文句が頻繁に暗号化されたため、既知平文(クリブ)として利用された。また、ローターの初期位置に「AAA」やキーボード配列の並び(「QWE」など)を使うオペレーターもいた。
さらに、ドイツの暗号専門家はケルクホフスの原則(「暗号システムの安全性は鍵の秘密にのみ依存すべきである」)に違反していた。ローターの内部配線の秘匿性に安全性の一部を依存していたが、配線はポーランドの暗号解読者によって数学的に解明された。
まとめ
| 項目 | 内容 |
|---|---|
| 分類 | 電気機械式多表式暗号(polyalphabetic cipher) |
| 発明者 | アルトゥール・シェルビウス(1918年特許出願) |
| 主要部品 | キーボード、プラグボード、ローター(3〜4個)、リフレクター、ランプボード |
| 鍵の構成 | ローター選択・配置、初期位置、リング設定、プラグボード接続 |
| 鍵空間 | 約 1.07 \times 10^{23}(≒ 77ビット) |
| 特徴 | 暗号化と復号が同一操作(自己逆性) |
| 弱点 | 文字が自身に暗号化されない、運用ミス |
| 解読 | ポーランド暗号局(1932年)、ブレッチリー・パーク(1940年〜) |
エニグマは古典暗号と現代暗号の転換点に位置する暗号システムである。その解読は暗号学に数学と計算機を本格導入する契機となり、チューリングの研究は後のコンピュータ科学の発展にもつながった。現代の暗号はエニグマの教訓を踏まえ、ケルクホフスの原則に基づいた設計が標準となっている。
実践
下のウィジェットで簡易エニグマを体験できる。歴史的なローター配線(ローターI〜III、リフレクターB)を使用している。暗号文を同じ設定で入力すれば平文に戻ることを確認してみよう。
Python実装
Pythonによる簡易エニグマ(ローターI〜III、リフレクターB、プラグボードなし)のシミュレーターを示す。
ROTORS = {
'I': 'EKMFLGDQVZNTOWYHXUSPAIBRCJ',
'II': 'AJDKSIRUXBLHWTMCQGZNPYFVOE',
'III': 'BDFHJLCPRTXVZNYEIWGAKMUSQO',
}
REFLECTOR_B = 'YRUHQSLDPXNGOKMIEBFZCWVJAT'
NOTCHES = {'I': 'Q', 'II': 'E', 'III': 'V'}
def c2n(c): return ord(c) - 65
def n2c(n): return chr(n % 26 + 65)
def fwd(c, w, off):
return n2c(c2n(w[(c2n(c) + off) % 26]) - off)
def inv(c, w, off):
return n2c(w.index(n2c((c2n(c) + off) % 26)) - off)
def enigma(text, rotors, positions):
r1, r2, r3 = rotors # right, middle, left
w1, w2, w3 = ROTORS[r1], ROTORS[r2], ROTORS[r3]
p = [c2n(c) for c in positions] # [right, middle, left]
result = []
for ch in text.upper():
if not ch.isalpha(): continue
# Step before encryption
if n2c(p[1]) == NOTCHES[r2]:
p[1] = (p[1]+1) % 26; p[2] = (p[2]+1) % 26
if n2c(p[0]) == NOTCHES[r1]:
p[1] = (p[1]+1) % 26
p[0] = (p[0]+1) % 26
# Forward through rotors
c = ch
c = fwd(c, w1, p[0])
c = fwd(c, w2, p[1])
c = fwd(c, w3, p[2])
c = REFLECTOR_B[c2n(c)] # Reflector
# Backward through rotors
c = inv(c, w3, p[2])
c = inv(c, w2, p[1])
c = inv(c, w1, p[0])
result.append(c)
return ''.join(result)
# 暗号化
ct = enigma("HELLOWORLD", ['I','II','III'], "AAA")
print(f"暗号化: HELLOWORLD -> {ct}")
# 復号(同じ設定で暗号文を入力)
pt = enigma(ct, ['I','II','III'], "AAA")
print(f"復号: {ct} -> {pt}")
# 自己暗号化しないことの確認
for ch in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
if enigma(ch, ['I','II','III'], "AAA") == ch:
print(f"NG: {ch}")
break
else:
print("全文字が自身と異なる文字に暗号化された")