見出し画像

エニグマ暗号はPythonで解読できるのだろうか?

背景

NHKの番組表を見ていた。番組は見ていないのだが内容は大体想像できる。
エニグマ暗号はPythonで解読できるのだろうか?

エニグマ暗号機とは

ここを参考
シーザー暗号、これを1文字ごとに、ずらし量を変えて行く暗号なわけです。

エニグマ暗号機をPythonで実装

この方のサイトのコードを改変しました。

スクリプト
ローターの数値を固定しました。ローターは3種類です。
ローターの初期位置を設定可能に変更しました。

import numpy as np
import random

class Enigma():
    def __init__(self, plane, alphabet, rotor1, rotor2, rotor3, plug_perm, ref_perm ,enigma_x, enigma_y, enigma_z):
        self.alphabet = alphabet
        self.char_dict = {x: n for n, x in enumerate(alphabet)}
        self.size = len(alphabet)
        self.identity = np.eye(self.size)
        self.plug_perm = plug_perm
        self.ref_perm = ref_perm
        self.rot1_perm = rotor1
        self.rot2_perm = rotor2
        self.rot3_perm = rotor3
        self.plane = plane
        self.enigma_x = enigma_x
        self.enigma_y = enigma_y
        self.enigma_z = enigma_z

    def get_matrix(self, cnt1, cnt2, cnt3):
        rot_rotor_perm = list(range(self.size))

        col_perm_mat = np.mat([self.identity[n - 1] for n in rot_rotor_perm])
        row_perm_mat = col_perm_mat.T

        plugbord = np.mat([self.identity[n] for n in self.plug_perm])
        reflector = np.mat([self.identity[n] for n in self.ref_perm])
        rotor1 = np.mat([self.identity[n] for n in self.rot1_perm])
        rotor2 = np.mat([self.identity[n] for n in self.rot2_perm])
        rotor3 = np.mat([self.identity[n] for n in self.rot3_perm])

        m = (plugbord *
             (row_perm_mat ** cnt1) * rotor1 * (col_perm_mat ** cnt1) *
             (row_perm_mat ** cnt2) * rotor2 * (col_perm_mat ** cnt2) *
             (row_perm_mat ** cnt3) * rotor3 * (col_perm_mat ** cnt3) *
             reflector *
             ((row_perm_mat ** cnt3) * rotor3 * (col_perm_mat ** cnt3)).I *
             ((row_perm_mat ** cnt2) * rotor2 * (col_perm_mat ** cnt2)).I *
             ((row_perm_mat ** cnt1) * rotor1 * (col_perm_mat ** cnt1)).I *
             plugbord)
        return m

    def get_text(self, text):
        after = ''
        cnt1 = self.enigma_x
        cnt2 = self.enigma_y
        cnt3 = self.enigma_z
        for char in text:
            if char not in self.alphabet:
                after += char
            else:
                vec = np.zeros(self.size)
                vec[self.char_dict[char]] = 1
                cnt1 += 1
                if cnt1 % self.size == 0:
                    cnt2 += 1
                if cnt2 % self.size == 0:
                    cnt3 += 1
                m = self.get_matrix(cnt1, cnt2, cnt3)
                vec = np.dot(vec, m).A[0]
                for n, x in enumerate(vec):
                    if int(x) == 1:
                        after += self.alphabet[n]
                        break
        return after

if __name__ == "__main__":
    alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    plane = 'HELLO WORLD. MY NAME IS ALICE. NICE TO MEET YOU.'
    plug_perm = [2, 1, 4, 3, 5, 6, 7, 8, 9, 16, 11, 12, 20, 14,
                 15, 10, 17, 18, 19, 13, 21, 22, 23, 24, 25, 26]
    ref_perm = [26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15,
                14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
    rotor1 = [19, 3, 13, 12, 14, 23, 4, 21, 25, 9, 5, 18, 24, 2, 20, 17, 6, 11, 7, 1, 15, 16, 10, 0, 8, 22]
    rotor2 = [1, 2, 19, 18, 20, 11, 0, 10, 7, 12, 23, 13, 9, 22, 21, 3, 5, 24, 8, 17, 25, 6, 4, 15, 16, 14]
    rotor3 = [18, 6, 0, 19, 22, 10, 24, 15, 21, 9, 7, 17, 16, 14, 3, 1, 23, 25, 4, 5, 11, 2, 8, 20, 13, 12]
    plug_perm = [x - 1 for x in plug_perm]
    ref_perm = [x - 1 for x in ref_perm]
    enigma = Enigma(plane, alphabet, rotor1, rotor2, rotor3, plug_perm, ref_perm, 3, 4, 5)
    cipher = enigma.get_text(plane)
    decrypt = enigma.get_text(cipher)
    print(f"Cipher Text: {cipher}")
    print(f"Decrypted Text: {decrypt}")

出力結果

Cipher Text:
MRQNT AJBTY. HP QXFO YK XPLIR. UQYI GS FKCH NTX.
Decrypted Text:
HELLO WORLD. MY NAME IS ALICE. NICE TO MEET YOU.

出力

HELLO→MRQNT
と暗号化されるわけです。

解読は?

選択暗号文攻撃 (CCA)を用います。任意の暗号文(ただし解読対象の暗号文は除く)に対応する平文が得られるとします。
「ミッドウェーで水が不足している」と嘘を敵に流せば、「ミッドウェー(Midway)」と「水(water)」の単語が推測されるわけです。
ローターとプラグボードは「何らかの手段」で入手することとします(沈んだ潜水艦からエニグマ暗号機を回収する映画がありましたよね。)

解析する必要があるのが初期のローターの回転位置(26x26x26)とローターの配置(6通り)

HELLO→MRQNT
となるローターの回転位置を総当たりで求めるとします。
コードは

    for x in range(len(alphabet)):
        for y in range(len(alphabet)):
            for z in range(len(alphabet)):
                enigma = Enigma(plane, alphabet, rotor1, rotor2, rotor3, plug_perm, ref_perm, x, y, z)
                cipher2 = enigma.get_text(plane)
                if decrypt[0:5] == enigma.get_text(cipher[0:5]):
                    print(f"Found matching setting: {decrypt[0:5]}")
                    print(x, y, z)

結果、

Cipher Text: MRQNT AJBTY. HP QXFO YK XPLIR. UQYI GS FKCH NTX.
Decrypted Text: HELLO WORLD. MY NAME IS ALICE. NICE TO MEET YOU.
First 5 chars of cipher: MRQNT
First 5 chars of decrypted: HELLO
Found matching setting: MRQNT
3 4 5

出力結果

数分で求めることができました。初期位置(3,4,5)なので。総当たりでも数10分でしょう。
ローターの配置(6通り)も総当たりで何とかなるのでは。
一旦、ローターの位置が解析されてしまえば、次の暗号のローターの初期位置を現在の暗号で送信するので、次の暗号もバレてしまうわけです。(軍事的にも公開鍵暗号方式の技術は必要なんですね。)

所感

>エニグマ暗号機とローターとプラグボードは「何らかの手段」で入手する
これが無理なのでしょうね。
ローターとプラグボードを入手できない場合は総当たりでは無理っぽいよね。

潜水艦から入手?(笑

とはいえ、エニグマ暗号機は敵に鹵獲されることを前提とした暗号機です。80年後は個人の計算機で総当たり攻撃できちゃうとは、当時は思ってもみなかったかもしれませんね。

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