Implement Enigma in python

at first

The sound of Enigma is cool, and I feel romance in cryptography regardless of past, present, or future. I thought that it would be a desire to make it and practice python, so I implemented it this time. There are many articles that I tried to implement in qiita, detailed explanation articles, and so on. The following is a particularly helpful article.

--I tried to implement Enigma https://qiita.com/opengl-8080/items/995778d1cce43ed5babc --Enigma cryptography that thinks with simple linear algebra https://qiita.com/tommyecguitar/items/5e07b622eaa329ed78a2 --Implementation of Enigma https://qiita.com/KentaKudo/items/8c0536ce684627b80fe5 --Enigma! !! https://qiita.com/deaikei/items/01e962c4c15b2efcc84f

What is Enigma?

Wikipedia will be helpful, including historical background.

--Enigma (cryptographic machine) (https://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%8B%E3%82%B0%E3%83%9E_(%E6%9A%97) % E5% 8F% B7% E6% A9% 9F))

Implementation procedure

Enigma cryptography that thinks with simple linear algebra https://qiita.com/tommyecguitar/items/5e07b622eaa329ed78a2 Was very helpful in executing the character replacement.

The following are the basics of implementation.

--Consider the alphabets A to Z as a 26-dimensional vector --A ・ ・ ・ (1, 0, 0, ・ ・ ・, 0) --B ・ ・ ・ (0, 1, 0, ・ ・ ・, 0) ―― ・ ・ ・ --Z ・ ・ ・ (0, 0, 0, ・ ・ ・, 1) --26 Create the next unit matrix $ I $ --To swap the $ i $ and $ j $ th alphabets, multiply the $ i $ line and the $ j $ line of $ I $ from the right. --Example --To swap letters A and B, multiply the matrix with the first and second rows of $ I $ swapped from the right. --To swap letters A and C, multiply the matrix with the first and third rows of $ I $ swapped from the right. --For simplicity, pass the plugboard and reflector settings as arguments --Use the random module to set the rotor

import numpy as np
import random


class Enigma():
    def __init__(self, plane, alphabet, plug_perm, ref_perm):
        self.alphabet = alphabet
        self.char_dict = {x: n + 1 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 = random.sample([n for n in range(1, 27)], self.size)
        self.rot2_perm = random.sample([n for n in range(1, 27)], self.size)
        self.rot3_perm = random.sample([n for n in range(1, 27)], self.size)
        self.plane = plane

Matrix of Enigma settings



    def get_matrix(self, cnt1, cnt2, cnt3):
        rot_rotor_perm = [(x + 1) % self.size for x in range(1, self.size + 1)]

        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 - 1] for n in self.plug_perm])
        reflector = np.mat([self.identity[n - 1] for n in self.ref_perm])
        rotor1 = np.mat([self.identity[n - 1] for n in self.rot1_perm])
        rotor2 = np.mat([self.identity[n - 1] for n in self.rot2_perm])
        rotor3 = np.mat([self.identity[n - 1] 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

Rotation of rotor, plaintext encryption

    def get_text(self, text):
        after = ''
        tmp = ''
        cnt1 = 0
        cnt2 = 0
        cnt3 = 0
        for char in text:
            tmp = ''
            if char not in self.alphabet:
                tmp += char
            else:
                vec = [0] * self.size
                vec[self.char_dict[char] - 1] = 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 = (vec * m).A[0]
                for n, x in enumerate(vec):
                    if int(x) == 1:
                        for key, value in self.char_dict.items():
                            if n == value - 1:
                                tmp = key
                                break
                        else:
                            continue
                        break
            after += tmp
        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]
    enigma = Enigma(plane, alphabet, plug_perm, ref_perm)
    cipher = enigma.get_text(plane)
    decrypt = enigma.get_text(cipher)
    print(cipher)
    print(decrypt)

Execution result

VNPKB NPJCN. CG GNXF QP JTRTS. DZKS BW UCMB NCW.
HELLO WORLD. MY NAME IS ALICE. NICE TO MEET YOU.

Fix 1

--- Corrected because you pointed out in the comment --The value of char_dict is modified to start with 0 --Along with this, all the parts that were n --1 are corrected to n. --col_perm_mat = np.mat ([self.identity [n --1] for n in rot_rotor_perm]) is required when turning the rotor, so fix it with n --1 --For character concatenation, stop line breaks with \ and concatenate with () --Simplified the part to get the following encrypted characters


for n, x in enumerate(vec):
    if int(x) == 1:
        tmp = self.alphabet[n]
        break

It's easier to read than it was before the fix.

class Enigma():
    def __init__(self, plane, alphabet, plug_perm, ref_perm):
        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 = random.sample(range(self.size), self.size)
        self.rot2_perm = random.sample(range(self.size), self.size)
        self.rot3_perm = random.sample(range(self.size), self.size)
        self.plane = plane

    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 = ''
        tmp = ''
        cnt1 = 0
        cnt2 = 0
        cnt3 = 0
        for char in text:
            tmp = ''
            if char not in self.alphabet:
                tmp += char
            else:
                vec = [0] * 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 = (vec * m).A[0]
                for n, x in enumerate(vec):
                    if int(x) == 1:
                        tmp = self.alphabet[n]
                        break
            after += tmp
        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]
    plug_perm = [x - 1 for x in plug_perm]
    ref_perm = [x - 1 for x in ref_perm]
    enigma = Enigma(plane, alphabet, plug_perm, ref_perm)
    cipher = enigma.get_text(plane)
    decrypt = enigma.get_text(cipher)
    print(cipher)
    print(decrypt)

Fix 2

--- Corrected because you pointed out in the comment --Immediately after rotor2 rotated 1/26, rotor3 had been rotated all the time.

cnt1 += 1
if cnt1 % self.size == 0:
    cnt2 += 1
if cnt2 % self.size == 0:
    cnt3 += 1
cnt1 += 1
if cnt1 % self.size == 0:
    cnt2 += 1
if cnt1 % self.size ** 2 == 0:
    cnt3 += 1

Fix 3

――How you told me in the comments --Achieve the following with divmod () --No need to increment cnt2 and cnt3 (use increment of cnt1) --No need for conditional branching regarding whether or not it can be divided by 26 --Always 0 $ \ leq $ cnt1. Cnt2. cnt3 $ \ leq $ 25

I would like to use this.

    def get_text(self, text):
        after = ''
        tmp = ''
        cnt1 = 0
        cnt2 = 0
        cnt3 = 0
        for char in text:
            tmp = ''
            if char not in self.alphabet:
                tmp += char
            else:
                vec = [0] * 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 = (vec * m).A[0]
                for n, x in enumerate(vec):
                    if int(x) == 1:
                        tmp = self.alphabet[n]
                        break
            after += tmp
        return after

--After correction (* The following is quoted from the comment section)

    def get_text(self, text):
        after = ''
        cnt = 0
        for char in text:
            if char not in self.alphabet:
                after += char
                continue
            vec = [0] * self.size
            vec[self.char_dict[char]] = 1
            cnt += 1
            cnt2, cnt1 = divmod(cnt, self.size)
            cnt3, cnt2 = divmod(cnt2, self.size)
            m = self.get_matrix(cnt1, cnt2, cnt3 % self.size)
            vec = (vec * m).A[0]
            for n, x in enumerate(vec):
                if int(x) == 1:
                    after += self.alphabet[n]
                    break
        return after

Reference article

--I implemented Enigma (https://qiita.com/opengl-8080/items/995778d1cce43ed5babc) --Enigma cryptography that thinks with simple linear algebra (https://qiita.com/tommyecguitar/items/5e07b622eaa329ed78a2) --Enigma implementation (https://qiita.com/KentaKudo/items/8c0536ce684627b80fe5) --Enigma! !! (Https://qiita.com/deaikei/items/01e962c4c15b2efcc84f) --Enigma (cryptographic machine) (https://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%8B%E3%82%B0%E3%83%9E_(%E6%9A%97) % E5% 8F% B7% E6% A9% 9F))

Recommended Posts

Implement Enigma in python
Implement recommendations in Python
Implement XENO in python
Implement sum in Python
Implement Traceroute in Python 3
Implement naive bayes in Python 3.3
Implement ancient ciphers in python
Implement Redis Mutex in Python
Implement extension field in Python
Implement fast RPC in Python
Implement method chain in Python
Implement Dijkstra's Algorithm in python
Implement Slack chatbot in Python
Implement stacking learning in Python [Kaggle]
Implement R's power.prop.test function in python
Implement the Singleton pattern in Python
Quickly implement REST API in Python
Quadtree in Python --2
Python in optimization
CURL in python
Metaprogramming in Python
Python 3.3 in Anaconda
Geocoding in python
SendKeys in Python
Meta-analysis in Python
Unittest in python
Epoch in Python
Discord in Python
Sudoku in Python
DCI in Python
quicksort in python
nCr in python
N-Gram in Python
Programming in python
Plink in Python
Constant in python
Lifegame in Python.
FizzBuzz in Python
Sqlite in python
StepAIC in Python
N-gram in python
LINE-Bot [0] in Python
Csv in python
Disassemble in Python
Reflection in Python
Constant in python
nCr in Python.
format in python
Scons in Python3
Puyo Puyo in python
python in virtualenv
PPAP in Python
Quad-tree in Python
Reflection in Python
Chemistry in Python
Hashable in python
DirectLiNGAM in Python
LiNGAM in Python
Flatten in python
flatten in python
I tried to implement PLSA in Python