enigmapython 1.3.3__tar.gz → 2.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {enigmapython-1.3.3 → enigmapython-2.0.1}/PKG-INFO +21 -2
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/Enigma.py +16 -3
- enigmapython-2.0.1/enigmapython/EnigmaK.py +13 -0
- enigmapython-2.0.1/enigmapython/EnigmaKRotorI.py +18 -0
- enigmapython-2.0.1/enigmapython/EnigmaKRotorII.py +18 -0
- enigmapython-2.0.1/enigmapython/EnigmaKRotorIII.py +18 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/Etw.py +0 -1
- enigmapython-2.0.1/enigmapython/EtwQWERTZ.py +7 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/Plugboard.py +0 -1
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/ReflectorUKWA.py +0 -1
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/ReflectorUKWB.py +0 -1
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/ReflectorUKWBThin.py +0 -1
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/ReflectorUKWC.py +0 -1
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/ReflectorUKWCThin.py +0 -1
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/ReflectorUKW_EnigmaB_A133.py +0 -1
- enigmapython-2.0.1/enigmapython/ReflectorUKW_EnigmaCommercial.py +15 -0
- enigmapython-1.3.3/enigmapython/ReflectorNorwayUKW.py → enigmapython-2.0.1/enigmapython/ReflectorUKW_EnigmaINorway.py +2 -3
- enigmapython-1.3.3/enigmapython/ReflectorSonderUKW.py → enigmapython-2.0.1/enigmapython/ReflectorUKW_EnigmaISonder.py +2 -3
- enigmapython-1.3.3/enigmapython/ReflectorZUKW.py → enigmapython-2.0.1/enigmapython/ReflectorUKW_EnigmaZ.py +1 -1
- enigmapython-2.0.1/enigmapython/RotatingReflector.py +5 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/Rotor.py +9 -12
- enigmapython-2.0.1/enigmapython/Scrambler.py +30 -0
- enigmapython-1.3.3/enigmapython/Scrambler.py → enigmapython-2.0.1/enigmapython/Settable.py +19 -37
- enigmapython-2.0.1/enigmapython/SettableReflector.py +8 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/Utils.py +34 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/XRay.py +3 -3
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython.egg-info/PKG-INFO +21 -2
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython.egg-info/SOURCES.txt +11 -6
- {enigmapython-1.3.3 → enigmapython-2.0.1}/setup.py +1 -1
- enigmapython-1.3.3/enigmapython/EnigmaDEtw_JWULCM.py +0 -6
- enigmapython-1.3.3/enigmapython/EnigmaDEtw_QWERTZ.py +0 -6
- enigmapython-1.3.3/enigmapython/ReflectorDUKW.py +0 -13
- enigmapython-1.3.3/enigmapython/RotatingReflector.py +0 -4
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/Alphabets.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/Clonable.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaB_A133.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaB_A133Etw.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaB_A133RotorI.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaB_A133RotorII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaB_A133RotorIII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaD.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaDRotorI.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaDRotorII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaDRotorIII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaI.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaINorway.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaINorwayRotorI.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaINorwayRotorII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaINorwayRotorIII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaINorwayRotorIV.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaINorwayRotorV.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaIRotorI.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaIRotorII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaIRotorIII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaIRotorIV.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaIRotorV.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaISonder.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaISonderRotorI.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaISonderRotorII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaISonderRotorIII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM3.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM3RotorI.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM3RotorII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM3RotorIII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM3RotorIV.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM3RotorV.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM3RotorVI.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM3RotorVII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM3RotorVIII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM4.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM4RotorBeta.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM4RotorGamma.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM4RotorI.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM4RotorII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM4RotorIII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM4RotorIV.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM4RotorV.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM4RotorVI.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM4RotorVII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaM4RotorVIII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaZ.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaZEtw.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaZRotorI.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaZRotorII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EnigmaZRotorIII.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/EtwPassthrough.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/Journaled.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/Observable.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/Observer.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/PlugboardPassthrough.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/Reflector.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/Swappable.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/SwappablePlugboard.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython/__init__.py +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython.egg-info/dependency_links.txt +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/enigmapython.egg-info/top_level.txt +0 -0
- {enigmapython-1.3.3 → enigmapython-2.0.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: enigmapython
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.1
|
|
4
4
|
Summary: A simple yet faithful library to emulate different Enigma machines models using Python
|
|
5
5
|
Home-page: https://github.com/denismaggior8/enigma-python
|
|
6
6
|
Author: Denis Maggiorotto
|
|
@@ -27,6 +27,15 @@ Dynamic: summary
|
|
|
27
27
|
|
|
28
28
|
Welcome to **enigmapython**, a Python package designed to emulate the legendary Enigma cryptographic machine used during World War II. **enigmapython** provides a faithful implementation of the Enigma machine, allowing users to explore and understand the workings of this historic device.
|
|
29
29
|
|
|
30
|
+
<br>
|
|
31
|
+
<div class="img-container" style="text-align: center;">
|
|
32
|
+
<a href="https://asciinema.org/a/761182">
|
|
33
|
+
<img src="https://asciinema.org/a/761182.svg" alt="asciicast"/>
|
|
34
|
+
</a>
|
|
35
|
+
</div>
|
|
36
|
+
<br>
|
|
37
|
+
|
|
38
|
+
|
|
30
39
|
This project is listed on [Wikipedia](https://en.wikipedia.org/wiki/List_of_Enigma_machine_simulators) as a globally recognized Enigma machine simulator, noted for its historical accuracy.
|
|
31
40
|
|
|
32
41
|
<div class="img-container" style="text-align: center;">
|
|
@@ -83,12 +92,22 @@ The following Enigma machine models (along with their rotors, reflectors and plu
|
|
|
83
92
|
|
|
84
93
|
*given the rarity of this model and the little documentation/simulators available, although I expect an encryption consistency on par with newer models, I was unable to test it as I would have liked
|
|
85
94
|
|
|
95
|
+
### Enigma K (Commercial Enigma)
|
|
96
|
+
|
|
97
|
+
| Scrambler | Wiring | Notch | Implemented |
|
|
98
|
+
|------- |---------------------------- |------- |------------- |
|
|
99
|
+
| ETW "QWERTZ" | qwertzuioasdfghjkpyxcvbnml | N/A | ✅ |
|
|
100
|
+
| Rotor I | lpgszmhaeoqkvxrfybutnicjdw | y | ✅ |
|
|
101
|
+
| Rotor II | slvgbtfxjqohewirzyamkpcndu | e | ✅ |
|
|
102
|
+
| Rotor III | cjgdpshkturawzxfmynqobvlie | n | ✅ |
|
|
103
|
+
| Reflector UKW | imetcgfraysqbzxwlhkdvupojn | N/A | ✅ |
|
|
104
|
+
|
|
105
|
+
|
|
86
106
|
### Enigma D
|
|
87
107
|
|
|
88
108
|
| Scrambler | Wiring | Notch | Implemented |
|
|
89
109
|
|------- |---------------------------- |------- |------------- |
|
|
90
110
|
| ETW "QWERTZ" | qwertzuioasdfghjkpyxcvbnml | N/A | ✅ |
|
|
91
|
-
| ETW "JWULCM" | jwulcmnohpqzyxiradkegvbtsf | N/A | ✅ |
|
|
92
111
|
| Rotor I | lpgszmhaeoqkvxrfybutnicjdw | y | ✅ |
|
|
93
112
|
| Rotor II | slvgbtfxjqohewirzyamkpcndu | e | ✅ |
|
|
94
113
|
| Rotor III | bdfhjlcprtxvznyeiwgakmusqo | n | ✅ |
|
|
@@ -3,6 +3,7 @@ from .RotatingReflector import RotatingReflector
|
|
|
3
3
|
from .Alphabets import Alphabets
|
|
4
4
|
from .Journaled import Journaled
|
|
5
5
|
from .Clonable import Clonable
|
|
6
|
+
from .Utils import Utils
|
|
6
7
|
import logging
|
|
7
8
|
|
|
8
9
|
|
|
@@ -41,6 +42,7 @@ class Enigma(Observer,Journaled,Clonable):
|
|
|
41
42
|
return output_string
|
|
42
43
|
|
|
43
44
|
def input_char(self,char):
|
|
45
|
+
char = char.lower()
|
|
44
46
|
logging.info("Input char: {}".format(char))
|
|
45
47
|
## Triggering rotors extra rotation due to double step issue
|
|
46
48
|
for rotor in self.rotors:
|
|
@@ -75,7 +77,11 @@ class Enigma(Observer,Journaled,Clonable):
|
|
|
75
77
|
def process_char(self, char):
|
|
76
78
|
scrambled_char = self.plugboard.scramble_char(self.plugboard.wiring,self.plugboard.alphabet_list.index(char),0)
|
|
77
79
|
logging.debug("Scrambled letter from plugboard: {}".format(scrambled_char))
|
|
78
|
-
|
|
80
|
+
|
|
81
|
+
# Calculate inverted wiring for ETW
|
|
82
|
+
inverted_wiring = Utils.inverse_string_permutation(self.etw.wiring, ''.join(self.etw.alphabet_list))
|
|
83
|
+
|
|
84
|
+
scrambled_char = self.etw.scramble_char(inverted_wiring, self.alphabet_list.index(scrambled_char), 0)
|
|
79
85
|
logging.debug("Scrambled letter from ETW: {}".format(scrambled_char))
|
|
80
86
|
iteration = 0
|
|
81
87
|
for rotor in self.rotors:
|
|
@@ -85,8 +91,14 @@ class Enigma(Observer,Journaled,Clonable):
|
|
|
85
91
|
scrambled_char = rotor.scramble_char(rotor.wiring,self.alphabet_list.index(scrambled_char)-self.rotors[iteration-1].position, rotor.position)
|
|
86
92
|
iteration +=1
|
|
87
93
|
logging.debug("Scrambled letter from rotor{}: {}".format(str(iteration),scrambled_char))
|
|
88
|
-
|
|
94
|
+
reflector_pos = getattr(self.reflector, 'position', 0)
|
|
95
|
+
scrambled_char = self.reflector.scramble_char(self.reflector.wiring,(self.alphabet_list.index(scrambled_char)-self.rotors[iteration-1].position), reflector_pos)
|
|
89
96
|
logging.debug("Scrambled letter from reflector: {}".format(scrambled_char))
|
|
97
|
+
|
|
98
|
+
# Adjust for reflector position on return path
|
|
99
|
+
if reflector_pos != 0:
|
|
100
|
+
scrambled_char = self.shift_letter(scrambled_char, -reflector_pos, self.alphabet_list)
|
|
101
|
+
|
|
90
102
|
for rotor in reversed(self.rotors):
|
|
91
103
|
if iteration == len(self.rotors):
|
|
92
104
|
scrambled_char = rotor.scramble_char(self.alphabet_list,(rotor.wiring.index(self.shift_letter(scrambled_char,rotor.position,self.alphabet_list))-rotor.position), rotor.position)
|
|
@@ -96,7 +108,8 @@ class Enigma(Observer,Journaled,Clonable):
|
|
|
96
108
|
logging.debug("Scrambled letter from rotor{}: {}".format(str(iteration+1),scrambled_char))
|
|
97
109
|
|
|
98
110
|
# Processing rotor 1 returning signal by ETW
|
|
99
|
-
|
|
111
|
+
# Processing rotor 1 returning signal by ETW
|
|
112
|
+
scrambled_char = self.etw.scramble_char(self.alphabet_list,(inverted_wiring.index(self.shift_letter(scrambled_char, (0 - self.rotors[iteration].position),self.alphabet_list))), 0)
|
|
100
113
|
logging.debug("Scrambled letter from ETW: {}".format(scrambled_char))
|
|
101
114
|
|
|
102
115
|
scrambled_char = self.plugboard.scramble_char(self.plugboard.wiring,self.plugboard.alphabet_list.index(scrambled_char),0)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .Enigma import Enigma
|
|
2
|
+
from .PlugboardPassthrough import PlugboardPassthrough
|
|
3
|
+
|
|
4
|
+
class EnigmaK(Enigma):
|
|
5
|
+
def __init__(self, rotor1, rotor2, rotor3, reflector, etw, auto_increment_rotors=True):
|
|
6
|
+
super().__init__(
|
|
7
|
+
plugboard=PlugboardPassthrough(),
|
|
8
|
+
rotors=[rotor1, rotor2, rotor3],
|
|
9
|
+
reflector=reflector,
|
|
10
|
+
etw=etw,
|
|
11
|
+
auto_increment_rotors=auto_increment_rotors
|
|
12
|
+
)
|
|
13
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from .Rotor import Rotor
|
|
2
|
+
from .Alphabets import Alphabets
|
|
3
|
+
|
|
4
|
+
class EnigmaKRotorI(Rotor):
|
|
5
|
+
|
|
6
|
+
wiring = 'lpgszmhaeoqkvxrfybutnicjdw'
|
|
7
|
+
notch_indexes = [24] # Y (notch position)
|
|
8
|
+
tag = "K_I"
|
|
9
|
+
|
|
10
|
+
def __init__(self, position = 0, ring = 0):
|
|
11
|
+
super().__init__(
|
|
12
|
+
wiring = self.wiring,
|
|
13
|
+
position=position,
|
|
14
|
+
ring=ring,
|
|
15
|
+
notch_indexes=self.notch_indexes,
|
|
16
|
+
alphabet=Alphabets.lookup.get("latin_i18n_26chars_lowercase")
|
|
17
|
+
)
|
|
18
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from .Rotor import Rotor
|
|
2
|
+
from .Alphabets import Alphabets
|
|
3
|
+
|
|
4
|
+
class EnigmaKRotorII(Rotor):
|
|
5
|
+
|
|
6
|
+
wiring = 'slvgbtfxjqohewirzyamkpcndu'
|
|
7
|
+
notch_indexes = [4] # E (notch position)
|
|
8
|
+
tag = "K_II"
|
|
9
|
+
|
|
10
|
+
def __init__(self, position = 0, ring = 0):
|
|
11
|
+
super().__init__(
|
|
12
|
+
wiring = self.wiring,
|
|
13
|
+
position=position,
|
|
14
|
+
ring=ring,
|
|
15
|
+
notch_indexes=self.notch_indexes,
|
|
16
|
+
alphabet=Alphabets.lookup.get("latin_i18n_26chars_lowercase")
|
|
17
|
+
)
|
|
18
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from .Rotor import Rotor
|
|
2
|
+
from .Alphabets import Alphabets
|
|
3
|
+
|
|
4
|
+
class EnigmaKRotorIII(Rotor):
|
|
5
|
+
|
|
6
|
+
wiring = 'cjgdpshkturawzxfmynqobvlie'
|
|
7
|
+
notch_indexes = [13] # N (position 13)
|
|
8
|
+
tag = "K_III"
|
|
9
|
+
|
|
10
|
+
def __init__(self, position = 0, ring = 0):
|
|
11
|
+
super().__init__(
|
|
12
|
+
wiring = self.wiring,
|
|
13
|
+
position=position,
|
|
14
|
+
ring=ring,
|
|
15
|
+
notch_indexes=self.notch_indexes,
|
|
16
|
+
alphabet=Alphabets.lookup.get("latin_i18n_26chars_lowercase")
|
|
17
|
+
)
|
|
18
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .SettableReflector import SettableReflector
|
|
2
|
+
from .Alphabets import Alphabets
|
|
3
|
+
|
|
4
|
+
class ReflectorUKW_EnigmaCommercial(SettableReflector):
|
|
5
|
+
"""Shared UKW reflector used by commercial Enigma D and Enigma K"""
|
|
6
|
+
|
|
7
|
+
wiring = 'imetcgfraysqbzxwlhkdvupojn'
|
|
8
|
+
|
|
9
|
+
def __init__(self, position=0, ring=0):
|
|
10
|
+
super().__init__(
|
|
11
|
+
wiring=self.wiring,
|
|
12
|
+
ring=ring,
|
|
13
|
+
position=position,
|
|
14
|
+
alphabet=Alphabets.lookup.get("latin_i18n_26chars_lowercase")
|
|
15
|
+
)
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
from .Rotor import Rotor
|
|
2
2
|
from .Reflector import Reflector
|
|
3
3
|
from .Alphabets import Alphabets
|
|
4
|
-
class
|
|
4
|
+
class ReflectorUKW_EnigmaINorway(Reflector):
|
|
5
5
|
|
|
6
6
|
wiring = 'mowjypuxndsraibfvlkzgqchet'
|
|
7
|
-
tag = "
|
|
7
|
+
tag = "Norway_UKW"
|
|
8
8
|
|
|
9
9
|
def __init__(self):
|
|
10
10
|
super().__init__(
|
|
11
11
|
wiring=self.wiring,
|
|
12
|
-
ring=0,
|
|
13
12
|
alphabet=Alphabets.lookup.get("latin_i18n_26chars_lowercase")
|
|
14
13
|
)
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
from .Alphabets import Alphabets
|
|
2
2
|
from .Reflector import Reflector
|
|
3
|
-
class
|
|
3
|
+
class ReflectorUKW_EnigmaISonder(Reflector):
|
|
4
4
|
|
|
5
5
|
wiring = 'ciagsndrbytpzfulvhekoqxwjm'
|
|
6
|
-
tag = "
|
|
6
|
+
tag = "Sonder_UKW"
|
|
7
7
|
|
|
8
8
|
def __init__(self):
|
|
9
9
|
super().__init__(
|
|
10
10
|
wiring=self.wiring,
|
|
11
|
-
ring=0,
|
|
12
11
|
alphabet=Alphabets.lookup.get("latin_i18n_26chars_lowercase")
|
|
13
12
|
)
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
from .Observable import Observable
|
|
2
2
|
from .Scrambler import Scrambler
|
|
3
|
+
from .Settable import Settable
|
|
3
4
|
import logging
|
|
4
5
|
|
|
5
|
-
class Rotor(Scrambler,Observable):
|
|
6
|
-
position = None
|
|
6
|
+
class Rotor(Scrambler,Observable,Settable):
|
|
7
7
|
rotations_counter = None
|
|
8
8
|
notch_indexes = None
|
|
9
9
|
double_step_triggered = None
|
|
10
10
|
|
|
11
|
-
def reset_position(self):
|
|
12
|
-
self.position = 0
|
|
13
|
-
|
|
14
11
|
def increment_position(self):
|
|
15
12
|
self.position = ((self.position + 1) % len(self.wiring))
|
|
16
13
|
self.rotations_counter = self.rotations_counter + 1
|
|
@@ -21,18 +18,18 @@ class Rotor(Scrambler,Observable):
|
|
|
21
18
|
self.notify_observers(None,None)
|
|
22
19
|
|
|
23
20
|
def set_position(self,position):
|
|
24
|
-
|
|
21
|
+
super().set_position(position % len(self.wiring))
|
|
25
22
|
self.rotations_counter = 0
|
|
26
23
|
|
|
27
24
|
def __init__(self, wiring, notch_indexes, alphabet, position = 0, ring = 0):
|
|
28
25
|
# Scrambler properties
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
)
|
|
26
|
+
# Scrambler properties
|
|
27
|
+
Scrambler.__init__(self, wiring=wiring, alphabet=alphabet)
|
|
28
|
+
# Settable properties
|
|
29
|
+
Settable.__init__(self, position=position % len(wiring), ring=ring)
|
|
30
|
+
self.set_ring(self.ring)
|
|
31
|
+
|
|
34
32
|
# Rotor properties
|
|
35
|
-
self.position = position % len(wiring)
|
|
36
33
|
self.notch_indexes = notch_indexes
|
|
37
34
|
self.double_step_triggered = False
|
|
38
35
|
self.rotations_counter = 0
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from .Journaled import Journaled
|
|
3
|
+
|
|
4
|
+
class Scrambler(Journaled):
|
|
5
|
+
wiring = None
|
|
6
|
+
alphabet_list = None
|
|
7
|
+
original_wiring = None
|
|
8
|
+
|
|
9
|
+
def scramble_char(self, dictionary, letter_index, shift):
|
|
10
|
+
output_char_index = dictionary.index(dictionary[(shift + letter_index) % len(dictionary)])
|
|
11
|
+
output_char = dictionary[output_char_index]
|
|
12
|
+
super().append_to_journal({
|
|
13
|
+
'output_char': output_char
|
|
14
|
+
})
|
|
15
|
+
return output_char
|
|
16
|
+
|
|
17
|
+
def __init__(self, wiring, alphabet):
|
|
18
|
+
#Journaled.__init__(self)
|
|
19
|
+
super().__init__()
|
|
20
|
+
self.wiring = wiring
|
|
21
|
+
self.original_wiring = self.wiring
|
|
22
|
+
self.alphabet_list = list(alphabet)
|
|
23
|
+
|
|
24
|
+
def __str__(self):
|
|
25
|
+
str = ""
|
|
26
|
+
for char in self.alphabet_list:
|
|
27
|
+
str += char
|
|
28
|
+
str += "\n"
|
|
29
|
+
str += "|" * len(self.alphabet_list)
|
|
30
|
+
return str
|
|
@@ -1,35 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class Scrambler(Journaled):
|
|
5
|
-
wiring = None
|
|
6
|
-
alphabet_list = None
|
|
7
|
-
dot_position = None
|
|
8
|
-
ring = None
|
|
9
|
-
set_scrambler_ring = None
|
|
10
|
-
original_wiring = None
|
|
11
|
-
|
|
12
|
-
def scramble_char(self, dictionary, letter_index, shift):
|
|
13
|
-
output_char_index = dictionary.index(dictionary[(shift + letter_index) % len(dictionary)])
|
|
14
|
-
output_char = dictionary[output_char_index]
|
|
15
|
-
super().append_to_journal({
|
|
16
|
-
'output_char': output_char
|
|
17
|
-
})
|
|
18
|
-
return output_char
|
|
1
|
+
class Settable:
|
|
2
|
+
position = 0
|
|
3
|
+
ring = 0
|
|
19
4
|
|
|
20
|
-
def __init__(self,
|
|
21
|
-
|
|
22
|
-
super().__init__()
|
|
23
|
-
self.wiring = wiring
|
|
24
|
-
self.original_wiring = self.wiring
|
|
25
|
-
self.alphabet_list = list(alphabet)
|
|
5
|
+
def __init__(self, position=0, ring=0):
|
|
6
|
+
self.position = position
|
|
26
7
|
self.ring = ring
|
|
27
|
-
|
|
8
|
+
|
|
9
|
+
def set_position(self, position):
|
|
10
|
+
self.position = position
|
|
28
11
|
|
|
12
|
+
def reset_position(self):
|
|
13
|
+
self.position = 0
|
|
29
14
|
|
|
30
|
-
def
|
|
15
|
+
def reset_ring(self):
|
|
16
|
+
self.set_ring(0)
|
|
17
|
+
|
|
18
|
+
def set_ring(self, ring):
|
|
19
|
+
self.ring = ring
|
|
31
20
|
self.wiring = self.original_wiring
|
|
32
21
|
self.dot_position = list(self.wiring).index(self.alphabet_list[0])
|
|
22
|
+
import logging
|
|
33
23
|
logging.debug("Dot position: " + str(self.dot_position))
|
|
34
24
|
for i in range(0, ring):
|
|
35
25
|
# Set temporary wiring variable
|
|
@@ -39,7 +29,7 @@ class Scrambler(Journaled):
|
|
|
39
29
|
# Loop over chars in temporary wiring
|
|
40
30
|
for char in temp_wiring:
|
|
41
31
|
# Shift the char by one and add that shifted char to wiring variable
|
|
42
|
-
wiring +=
|
|
32
|
+
wiring += self._shift(char, 1, self.alphabet_list)
|
|
43
33
|
# Add one to dot position, make sure we don't exceed the lenght of the alphabet
|
|
44
34
|
self.wiring = wiring
|
|
45
35
|
self.dot_position = (self.dot_position + 1) % len(self.alphabet_list)
|
|
@@ -53,17 +43,9 @@ class Scrambler(Journaled):
|
|
|
53
43
|
self.wiring = self.wiring[-1:] + self.wiring[:-1]
|
|
54
44
|
# The following line has been commente out becode not Micropython compatible
|
|
55
45
|
# logging.debug("Rotation " + str(i).zfill(2) + "; Wiring: " + self.wiring)
|
|
56
|
-
|
|
46
|
+
|
|
57
47
|
@staticmethod
|
|
58
|
-
def
|
|
48
|
+
def _shift(letter, shift, alphabet_list):
|
|
59
49
|
for i in range(0, len(alphabet_list)):
|
|
60
50
|
if alphabet_list[i] == letter:
|
|
61
51
|
return alphabet_list[(i + shift) % len(alphabet_list)]
|
|
62
|
-
|
|
63
|
-
def __str__(self):
|
|
64
|
-
str = ""
|
|
65
|
-
for char in self.alphabet_list:
|
|
66
|
-
str += char
|
|
67
|
-
str += "\n"
|
|
68
|
-
str += "|" * len(self.alphabet_list)
|
|
69
|
-
return str
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from .Reflector import Reflector
|
|
2
|
+
from .Settable import Settable
|
|
3
|
+
|
|
4
|
+
class SettableReflector(Reflector, Settable):
|
|
5
|
+
def __init__(self, wiring, alphabet, position=0, ring=0):
|
|
6
|
+
Reflector.__init__(self, wiring=wiring, alphabet=alphabet)
|
|
7
|
+
Settable.__init__(self, position=position, ring=ring)
|
|
8
|
+
self.set_ring(self.ring)
|
|
@@ -48,4 +48,38 @@ class Utils:
|
|
|
48
48
|
else:
|
|
49
49
|
str_list.append(char)
|
|
50
50
|
return ''.join(str_list)
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def inverse_string_permutation(string: str, alphabet: str = None) -> str:
|
|
54
|
+
"""
|
|
55
|
+
Compute the inverse permutation of a string for any alphabet.
|
|
56
|
+
|
|
57
|
+
string : str
|
|
58
|
+
A string mapping input -> output
|
|
59
|
+
alphabet : str, optional
|
|
60
|
+
The alphabet to use. Defaults to the characters in string length,
|
|
61
|
+
starting with the first character of string (e.g., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
str
|
|
66
|
+
The inverse string
|
|
67
|
+
"""
|
|
68
|
+
n = len(string)
|
|
69
|
+
|
|
70
|
+
# If no alphabet is provided, assume consecutive characters starting from string[0]
|
|
71
|
+
if alphabet is None:
|
|
72
|
+
alphabet = ''.join(chr(ord(string[0]) + i) for i in range(n))
|
|
73
|
+
|
|
74
|
+
# Build a dictionary for quick index lookup
|
|
75
|
+
char_to_index = {char: i for i, char in enumerate(alphabet)}
|
|
76
|
+
|
|
77
|
+
# Initialize inverse list
|
|
78
|
+
inverse = [''] * n
|
|
79
|
+
|
|
80
|
+
for i, out_char in enumerate(string):
|
|
81
|
+
index = char_to_index[out_char]
|
|
82
|
+
inverse[index] = alphabet[i]
|
|
83
|
+
|
|
84
|
+
return ''.join(inverse)
|
|
51
85
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from rich.console import Console
|
|
2
2
|
from rich.text import Text
|
|
3
3
|
|
|
4
|
-
from enigmapython.
|
|
4
|
+
from enigmapython.SettableReflector import SettableReflector
|
|
5
5
|
|
|
6
6
|
class XRay():
|
|
7
7
|
@staticmethod
|
|
@@ -56,8 +56,8 @@ class XRay():
|
|
|
56
56
|
| | {rotor_walls} | | | |
|
|
57
57
|
+-----+ {rotor_walls_bottom} +-----+ +-----+
|
|
58
58
|
|
|
59
|
-
Pos.: {"{} ({:02})".format(enigma.alphabet_list[enigma.reflector.position].upper(),enigma.reflector.position) if isinstance(enigma.reflector,
|
|
60
|
-
Ring: {"{} ({:02})".format(enigma.alphabet_list[enigma.reflector.ring].upper(),enigma.reflector.ring)} {rotors_rings}
|
|
59
|
+
Pos.: {"{} ({:02})".format(enigma.alphabet_list[enigma.reflector.position].upper(),enigma.reflector.position) if isinstance(enigma.reflector, SettableReflector) else ' N/A '} {rotors_positions}
|
|
60
|
+
Ring: {"{} ({:02})".format(enigma.alphabet_list[enigma.reflector.ring].upper(),enigma.reflector.ring) if isinstance(enigma.reflector, SettableReflector) else ' N/A '} {rotors_rings}
|
|
61
61
|
"""
|
|
62
62
|
console.print(Text(diagram, style="bold"))
|
|
63
63
|
return Text(diagram, style="bold")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: enigmapython
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.1
|
|
4
4
|
Summary: A simple yet faithful library to emulate different Enigma machines models using Python
|
|
5
5
|
Home-page: https://github.com/denismaggior8/enigma-python
|
|
6
6
|
Author: Denis Maggiorotto
|
|
@@ -27,6 +27,15 @@ Dynamic: summary
|
|
|
27
27
|
|
|
28
28
|
Welcome to **enigmapython**, a Python package designed to emulate the legendary Enigma cryptographic machine used during World War II. **enigmapython** provides a faithful implementation of the Enigma machine, allowing users to explore and understand the workings of this historic device.
|
|
29
29
|
|
|
30
|
+
<br>
|
|
31
|
+
<div class="img-container" style="text-align: center;">
|
|
32
|
+
<a href="https://asciinema.org/a/761182">
|
|
33
|
+
<img src="https://asciinema.org/a/761182.svg" alt="asciicast"/>
|
|
34
|
+
</a>
|
|
35
|
+
</div>
|
|
36
|
+
<br>
|
|
37
|
+
|
|
38
|
+
|
|
30
39
|
This project is listed on [Wikipedia](https://en.wikipedia.org/wiki/List_of_Enigma_machine_simulators) as a globally recognized Enigma machine simulator, noted for its historical accuracy.
|
|
31
40
|
|
|
32
41
|
<div class="img-container" style="text-align: center;">
|
|
@@ -83,12 +92,22 @@ The following Enigma machine models (along with their rotors, reflectors and plu
|
|
|
83
92
|
|
|
84
93
|
*given the rarity of this model and the little documentation/simulators available, although I expect an encryption consistency on par with newer models, I was unable to test it as I would have liked
|
|
85
94
|
|
|
95
|
+
### Enigma K (Commercial Enigma)
|
|
96
|
+
|
|
97
|
+
| Scrambler | Wiring | Notch | Implemented |
|
|
98
|
+
|------- |---------------------------- |------- |------------- |
|
|
99
|
+
| ETW "QWERTZ" | qwertzuioasdfghjkpyxcvbnml | N/A | ✅ |
|
|
100
|
+
| Rotor I | lpgszmhaeoqkvxrfybutnicjdw | y | ✅ |
|
|
101
|
+
| Rotor II | slvgbtfxjqohewirzyamkpcndu | e | ✅ |
|
|
102
|
+
| Rotor III | cjgdpshkturawzxfmynqobvlie | n | ✅ |
|
|
103
|
+
| Reflector UKW | imetcgfraysqbzxwlhkdvupojn | N/A | ✅ |
|
|
104
|
+
|
|
105
|
+
|
|
86
106
|
### Enigma D
|
|
87
107
|
|
|
88
108
|
| Scrambler | Wiring | Notch | Implemented |
|
|
89
109
|
|------- |---------------------------- |------- |------------- |
|
|
90
110
|
| ETW "QWERTZ" | qwertzuioasdfghjkpyxcvbnml | N/A | ✅ |
|
|
91
|
-
| ETW "JWULCM" | jwulcmnohpqzyxiradkegvbtsf | N/A | ✅ |
|
|
92
111
|
| Rotor I | lpgszmhaeoqkvxrfybutnicjdw | y | ✅ |
|
|
93
112
|
| Rotor II | slvgbtfxjqohewirzyamkpcndu | e | ✅ |
|
|
94
113
|
| Rotor III | bdfhjlcprtxvznyeiwgakmusqo | n | ✅ |
|
|
@@ -8,8 +8,6 @@ enigmapython/EnigmaB_A133RotorI.py
|
|
|
8
8
|
enigmapython/EnigmaB_A133RotorII.py
|
|
9
9
|
enigmapython/EnigmaB_A133RotorIII.py
|
|
10
10
|
enigmapython/EnigmaD.py
|
|
11
|
-
enigmapython/EnigmaDEtw_JWULCM.py
|
|
12
|
-
enigmapython/EnigmaDEtw_QWERTZ.py
|
|
13
11
|
enigmapython/EnigmaDRotorI.py
|
|
14
12
|
enigmapython/EnigmaDRotorII.py
|
|
15
13
|
enigmapython/EnigmaDRotorIII.py
|
|
@@ -29,6 +27,10 @@ enigmapython/EnigmaISonder.py
|
|
|
29
27
|
enigmapython/EnigmaISonderRotorI.py
|
|
30
28
|
enigmapython/EnigmaISonderRotorII.py
|
|
31
29
|
enigmapython/EnigmaISonderRotorIII.py
|
|
30
|
+
enigmapython/EnigmaK.py
|
|
31
|
+
enigmapython/EnigmaKRotorI.py
|
|
32
|
+
enigmapython/EnigmaKRotorII.py
|
|
33
|
+
enigmapython/EnigmaKRotorIII.py
|
|
32
34
|
enigmapython/EnigmaM3.py
|
|
33
35
|
enigmapython/EnigmaM3RotorI.py
|
|
34
36
|
enigmapython/EnigmaM3RotorII.py
|
|
@@ -56,25 +58,28 @@ enigmapython/EnigmaZRotorII.py
|
|
|
56
58
|
enigmapython/EnigmaZRotorIII.py
|
|
57
59
|
enigmapython/Etw.py
|
|
58
60
|
enigmapython/EtwPassthrough.py
|
|
61
|
+
enigmapython/EtwQWERTZ.py
|
|
59
62
|
enigmapython/Journaled.py
|
|
60
63
|
enigmapython/Observable.py
|
|
61
64
|
enigmapython/Observer.py
|
|
62
65
|
enigmapython/Plugboard.py
|
|
63
66
|
enigmapython/PlugboardPassthrough.py
|
|
64
67
|
enigmapython/Reflector.py
|
|
65
|
-
enigmapython/ReflectorDUKW.py
|
|
66
|
-
enigmapython/ReflectorNorwayUKW.py
|
|
67
|
-
enigmapython/ReflectorSonderUKW.py
|
|
68
68
|
enigmapython/ReflectorUKWA.py
|
|
69
69
|
enigmapython/ReflectorUKWB.py
|
|
70
70
|
enigmapython/ReflectorUKWBThin.py
|
|
71
71
|
enigmapython/ReflectorUKWC.py
|
|
72
72
|
enigmapython/ReflectorUKWCThin.py
|
|
73
73
|
enigmapython/ReflectorUKW_EnigmaB_A133.py
|
|
74
|
-
enigmapython/
|
|
74
|
+
enigmapython/ReflectorUKW_EnigmaCommercial.py
|
|
75
|
+
enigmapython/ReflectorUKW_EnigmaINorway.py
|
|
76
|
+
enigmapython/ReflectorUKW_EnigmaISonder.py
|
|
77
|
+
enigmapython/ReflectorUKW_EnigmaZ.py
|
|
75
78
|
enigmapython/RotatingReflector.py
|
|
76
79
|
enigmapython/Rotor.py
|
|
77
80
|
enigmapython/Scrambler.py
|
|
81
|
+
enigmapython/Settable.py
|
|
82
|
+
enigmapython/SettableReflector.py
|
|
78
83
|
enigmapython/Swappable.py
|
|
79
84
|
enigmapython/SwappablePlugboard.py
|
|
80
85
|
enigmapython/Utils.py
|
|
@@ -12,7 +12,7 @@ setup(
|
|
|
12
12
|
long_description_content_type='text/markdown',
|
|
13
13
|
url="https://github.com/denismaggior8/enigma-python",
|
|
14
14
|
name="enigmapython",
|
|
15
|
-
version="
|
|
15
|
+
version="2.0.1",
|
|
16
16
|
packages=find_packages(
|
|
17
17
|
# All keyword arguments below are optional:
|
|
18
18
|
where='.', # '.' by default
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
from .Reflector import Reflector
|
|
2
|
-
from .Alphabets import Alphabets
|
|
3
|
-
class ReflectorDUKW(Reflector):
|
|
4
|
-
|
|
5
|
-
wiring = 'imetcgfraysqbzxwlhkdvupojn'
|
|
6
|
-
tag = "D_UKW"
|
|
7
|
-
|
|
8
|
-
def __init__(self):
|
|
9
|
-
super().__init__(
|
|
10
|
-
wiring=self.wiring,
|
|
11
|
-
ring=0,
|
|
12
|
-
alphabet=Alphabets.lookup.get("latin_i18n_26chars_lowercase")
|
|
13
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|