# 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 =  * 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
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 =  * 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
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.

• Before correction
``````cnt1 += 1
if cnt1 % self.size == 0:
cnt2 += 1
if cnt2 % self.size == 0:
cnt3 += 1
``````
• Revised
``````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.

• Before correction
``````    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 =  * 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
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 =  * 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
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))