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
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))
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
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
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.
--- 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)
--- 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
――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
--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