enigmapython 1.3.2__tar.gz → 2.0.0__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.2 → enigmapython-2.0.0}/PKG-INFO +25 -3
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/Enigma.py +16 -3
- enigmapython-2.0.0/enigmapython/EnigmaDEtw_QWERTZ.py +5 -0
- enigmapython-2.0.0/enigmapython/EnigmaK.py +13 -0
- enigmapython-2.0.0/enigmapython/EnigmaKEtw_QWERTZ.py +5 -0
- enigmapython-2.0.0/enigmapython/EnigmaKRotorI.py +18 -0
- enigmapython-2.0.0/enigmapython/EnigmaKRotorII.py +18 -0
- enigmapython-2.0.0/enigmapython/EnigmaKRotorIII.py +18 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/Etw.py +0 -1
- enigmapython-2.0.0/enigmapython/EtwQWERTZ.py +7 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/Plugboard.py +0 -1
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/ReflectorUKWA.py +0 -1
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/ReflectorUKWB.py +0 -1
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/ReflectorUKWBThin.py +0 -1
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/ReflectorUKWC.py +0 -1
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/ReflectorUKWCThin.py +0 -1
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/ReflectorUKW_EnigmaB_A133.py +0 -1
- enigmapython-2.0.0/enigmapython/ReflectorUKW_EnigmaCommercial.py +15 -0
- enigmapython-1.3.2/enigmapython/ReflectorNorwayUKW.py → enigmapython-2.0.0/enigmapython/ReflectorUKW_EnigmaINorway.py +2 -3
- enigmapython-1.3.2/enigmapython/ReflectorSonderUKW.py → enigmapython-2.0.0/enigmapython/ReflectorUKW_EnigmaISonder.py +2 -3
- enigmapython-1.3.2/enigmapython/ReflectorZUKW.py → enigmapython-2.0.0/enigmapython/ReflectorUKW_EnigmaZ.py +1 -1
- enigmapython-2.0.0/enigmapython/RotatingReflector.py +5 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/Rotor.py +9 -12
- enigmapython-2.0.0/enigmapython/Scrambler.py +30 -0
- enigmapython-1.3.2/enigmapython/Scrambler.py → enigmapython-2.0.0/enigmapython/Settable.py +19 -37
- enigmapython-2.0.0/enigmapython/SettableReflector.py +8 -0
- enigmapython-2.0.0/enigmapython/Utils.py +85 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/XRay.py +3 -3
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython.egg-info/PKG-INFO +25 -3
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython.egg-info/SOURCES.txt +12 -5
- {enigmapython-1.3.2 → enigmapython-2.0.0}/setup.py +1 -1
- enigmapython-1.3.2/enigmapython/EnigmaDEtw_JWULCM.py +0 -6
- enigmapython-1.3.2/enigmapython/EnigmaDEtw_QWERTZ.py +0 -6
- enigmapython-1.3.2/enigmapython/ReflectorDUKW.py +0 -13
- enigmapython-1.3.2/enigmapython/RotatingReflector.py +0 -4
- enigmapython-1.3.2/enigmapython/Utils.py +0 -51
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/Alphabets.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/Clonable.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaB_A133.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaB_A133Etw.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaB_A133RotorI.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaB_A133RotorII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaB_A133RotorIII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaD.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaDRotorI.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaDRotorII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaDRotorIII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaI.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaINorway.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaINorwayRotorI.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaINorwayRotorII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaINorwayRotorIII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaINorwayRotorIV.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaINorwayRotorV.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaIRotorI.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaIRotorII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaIRotorIII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaIRotorIV.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaIRotorV.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaISonder.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaISonderRotorI.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaISonderRotorII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaISonderRotorIII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM3.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM3RotorI.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM3RotorII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM3RotorIII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM3RotorIV.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM3RotorV.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM3RotorVI.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM3RotorVII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM3RotorVIII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM4.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM4RotorBeta.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM4RotorGamma.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM4RotorI.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM4RotorII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM4RotorIII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM4RotorIV.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM4RotorV.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM4RotorVI.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM4RotorVII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaM4RotorVIII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaZ.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaZEtw.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaZRotorI.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaZRotorII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EnigmaZRotorIII.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/EtwPassthrough.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/Journaled.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/Observable.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/Observer.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/PlugboardPassthrough.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/Reflector.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/Swappable.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/SwappablePlugboard.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython/__init__.py +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython.egg-info/dependency_links.txt +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/enigmapython.egg-info/top_level.txt +0 -0
- {enigmapython-1.3.2 → enigmapython-2.0.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: enigmapython
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
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;">
|
|
@@ -65,6 +74,7 @@ This project is listed on [Wikipedia](https://en.wikipedia.org/wiki/List_of_Enig
|
|
|
65
74
|
|
|
66
75
|
|
|
67
76
|
- [Authentic German Army Test Message from 1930](https://cryptocellar.org/enigma/e-message-1930.html): Documented by Frode Weierud's CryptoCellar, this test validates that the **Enigma I** implementation correctly handles the complex interaction of rotors, ring settings, and plugboard connections exactly as the original machines did. See the [corresponding unit test](https://github.com/denismaggior8/enigma-python/blob/master/tests/Enigma1930_tests.py) for details.
|
|
77
|
+
- [Authentic U-534 M4 Message P1030700](https://enigma.hoerenberg.com/index.php?cat=The%20U534%20messages&page=P1030700): A message from the U-534 submarine (May 1945), validating the **Enigma M4** implementation (including the Greek rotor and thin reflector) against historical intercepts. See the [corresponding unit test](https://github.com/denismaggior8/enigma-python/blob/master/tests/EnigmaM4_U534_tests.py) for details.
|
|
68
78
|
|
|
69
79
|
## Machines implementations
|
|
70
80
|
|
|
@@ -82,12 +92,22 @@ The following Enigma machine models (along with their rotors, reflectors and plu
|
|
|
82
92
|
|
|
83
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
|
|
84
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
|
+
|
|
85
106
|
### Enigma D
|
|
86
107
|
|
|
87
108
|
| Scrambler | Wiring | Notch | Implemented |
|
|
88
109
|
|------- |---------------------------- |------- |------------- |
|
|
89
110
|
| ETW "QWERTZ" | qwertzuioasdfghjkpyxcvbnml | N/A | ✅ |
|
|
90
|
-
| ETW "JWULCM" | jwulcmnohpqzyxiradkegvbtsf | N/A | ✅ |
|
|
91
111
|
| Rotor I | lpgszmhaeoqkvxrfybutnicjdw | y | ✅ |
|
|
92
112
|
| Rotor II | slvgbtfxjqohewirzyamkpcndu | e | ✅ |
|
|
93
113
|
| Rotor III | bdfhjlcprtxvznyeiwgakmusqo | n | ✅ |
|
|
@@ -191,7 +211,9 @@ Get started by installing the package from PyPI (`pip install enigmapython`) and
|
|
|
191
211
|
|
|
192
212
|
## Documentation
|
|
193
213
|
|
|
194
|
-
|
|
214
|
+
Full API documentation is available on [ReadTheDocs](https://enigmapython.readthedocs.io/).
|
|
215
|
+
|
|
216
|
+
For additional details, you can also refer to the [local documentation](./docs/README.md), examples, and code comments.
|
|
195
217
|
|
|
196
218
|
## Known implementations
|
|
197
219
|
|
|
@@ -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)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
|
|
2
|
+
class Utils:
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@staticmethod
|
|
6
|
+
def find_divergence(str1, str2):
|
|
7
|
+
"""
|
|
8
|
+
Finds the index where two strings first diverge,
|
|
9
|
+
and the characters at that index in both strings.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
str1 (str): The first string.
|
|
13
|
+
str2 (str): The second string.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
tuple: (index, char1, char2) where index is the position of divergence,
|
|
17
|
+
char1 is the character in str1 at the divergence,
|
|
18
|
+
and char2 is the character in str2 at the divergence.
|
|
19
|
+
If the strings only diverge in length, char1 and char2 will be None.
|
|
20
|
+
If the strings are identical, returns None.
|
|
21
|
+
"""
|
|
22
|
+
# Iterate through both strings up to the length of the shorter string
|
|
23
|
+
for i in range(min(len(str1), len(str2))):
|
|
24
|
+
if str1[i] != str2[i]:
|
|
25
|
+
return i, str1[i], str2[i] # Return the index and differing characters
|
|
26
|
+
|
|
27
|
+
# If the strings differ in length
|
|
28
|
+
if len(str1) != len(str2):
|
|
29
|
+
return min(len(str1), len(str2)), None, None
|
|
30
|
+
|
|
31
|
+
# If the strings are identical
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def find_all_subclasses(cls):
|
|
36
|
+
return set(cls.__subclasses__()).union(
|
|
37
|
+
[s for c in cls.__subclasses__() for s in Utils.find_all_subclasses(c)])
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def swap_chars(string, ch1, ch2):
|
|
41
|
+
if ch1 == ch2: return string
|
|
42
|
+
str_list = []
|
|
43
|
+
for char in string:
|
|
44
|
+
if char == ch1:
|
|
45
|
+
str_list.append(ch2)
|
|
46
|
+
elif char == ch2:
|
|
47
|
+
str_list.append(ch1)
|
|
48
|
+
else:
|
|
49
|
+
str_list.append(char)
|
|
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)
|
|
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.0
|
|
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;">
|
|
@@ -65,6 +74,7 @@ This project is listed on [Wikipedia](https://en.wikipedia.org/wiki/List_of_Enig
|
|
|
65
74
|
|
|
66
75
|
|
|
67
76
|
- [Authentic German Army Test Message from 1930](https://cryptocellar.org/enigma/e-message-1930.html): Documented by Frode Weierud's CryptoCellar, this test validates that the **Enigma I** implementation correctly handles the complex interaction of rotors, ring settings, and plugboard connections exactly as the original machines did. See the [corresponding unit test](https://github.com/denismaggior8/enigma-python/blob/master/tests/Enigma1930_tests.py) for details.
|
|
77
|
+
- [Authentic U-534 M4 Message P1030700](https://enigma.hoerenberg.com/index.php?cat=The%20U534%20messages&page=P1030700): A message from the U-534 submarine (May 1945), validating the **Enigma M4** implementation (including the Greek rotor and thin reflector) against historical intercepts. See the [corresponding unit test](https://github.com/denismaggior8/enigma-python/blob/master/tests/EnigmaM4_U534_tests.py) for details.
|
|
68
78
|
|
|
69
79
|
## Machines implementations
|
|
70
80
|
|
|
@@ -82,12 +92,22 @@ The following Enigma machine models (along with their rotors, reflectors and plu
|
|
|
82
92
|
|
|
83
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
|
|
84
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
|
+
|
|
85
106
|
### Enigma D
|
|
86
107
|
|
|
87
108
|
| Scrambler | Wiring | Notch | Implemented |
|
|
88
109
|
|------- |---------------------------- |------- |------------- |
|
|
89
110
|
| ETW "QWERTZ" | qwertzuioasdfghjkpyxcvbnml | N/A | ✅ |
|
|
90
|
-
| ETW "JWULCM" | jwulcmnohpqzyxiradkegvbtsf | N/A | ✅ |
|
|
91
111
|
| Rotor I | lpgszmhaeoqkvxrfybutnicjdw | y | ✅ |
|
|
92
112
|
| Rotor II | slvgbtfxjqohewirzyamkpcndu | e | ✅ |
|
|
93
113
|
| Rotor III | bdfhjlcprtxvznyeiwgakmusqo | n | ✅ |
|
|
@@ -191,7 +211,9 @@ Get started by installing the package from PyPI (`pip install enigmapython`) and
|
|
|
191
211
|
|
|
192
212
|
## Documentation
|
|
193
213
|
|
|
194
|
-
|
|
214
|
+
Full API documentation is available on [ReadTheDocs](https://enigmapython.readthedocs.io/).
|
|
215
|
+
|
|
216
|
+
For additional details, you can also refer to the [local documentation](./docs/README.md), examples, and code comments.
|
|
195
217
|
|
|
196
218
|
## Known implementations
|
|
197
219
|
|
|
@@ -8,7 +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
11
|
enigmapython/EnigmaDEtw_QWERTZ.py
|
|
13
12
|
enigmapython/EnigmaDRotorI.py
|
|
14
13
|
enigmapython/EnigmaDRotorII.py
|
|
@@ -29,6 +28,11 @@ enigmapython/EnigmaISonder.py
|
|
|
29
28
|
enigmapython/EnigmaISonderRotorI.py
|
|
30
29
|
enigmapython/EnigmaISonderRotorII.py
|
|
31
30
|
enigmapython/EnigmaISonderRotorIII.py
|
|
31
|
+
enigmapython/EnigmaK.py
|
|
32
|
+
enigmapython/EnigmaKEtw_QWERTZ.py
|
|
33
|
+
enigmapython/EnigmaKRotorI.py
|
|
34
|
+
enigmapython/EnigmaKRotorII.py
|
|
35
|
+
enigmapython/EnigmaKRotorIII.py
|
|
32
36
|
enigmapython/EnigmaM3.py
|
|
33
37
|
enigmapython/EnigmaM3RotorI.py
|
|
34
38
|
enigmapython/EnigmaM3RotorII.py
|
|
@@ -56,25 +60,28 @@ enigmapython/EnigmaZRotorII.py
|
|
|
56
60
|
enigmapython/EnigmaZRotorIII.py
|
|
57
61
|
enigmapython/Etw.py
|
|
58
62
|
enigmapython/EtwPassthrough.py
|
|
63
|
+
enigmapython/EtwQWERTZ.py
|
|
59
64
|
enigmapython/Journaled.py
|
|
60
65
|
enigmapython/Observable.py
|
|
61
66
|
enigmapython/Observer.py
|
|
62
67
|
enigmapython/Plugboard.py
|
|
63
68
|
enigmapython/PlugboardPassthrough.py
|
|
64
69
|
enigmapython/Reflector.py
|
|
65
|
-
enigmapython/ReflectorDUKW.py
|
|
66
|
-
enigmapython/ReflectorNorwayUKW.py
|
|
67
|
-
enigmapython/ReflectorSonderUKW.py
|
|
68
70
|
enigmapython/ReflectorUKWA.py
|
|
69
71
|
enigmapython/ReflectorUKWB.py
|
|
70
72
|
enigmapython/ReflectorUKWBThin.py
|
|
71
73
|
enigmapython/ReflectorUKWC.py
|
|
72
74
|
enigmapython/ReflectorUKWCThin.py
|
|
73
75
|
enigmapython/ReflectorUKW_EnigmaB_A133.py
|
|
74
|
-
enigmapython/
|
|
76
|
+
enigmapython/ReflectorUKW_EnigmaCommercial.py
|
|
77
|
+
enigmapython/ReflectorUKW_EnigmaINorway.py
|
|
78
|
+
enigmapython/ReflectorUKW_EnigmaISonder.py
|
|
79
|
+
enigmapython/ReflectorUKW_EnigmaZ.py
|
|
75
80
|
enigmapython/RotatingReflector.py
|
|
76
81
|
enigmapython/Rotor.py
|
|
77
82
|
enigmapython/Scrambler.py
|
|
83
|
+
enigmapython/Settable.py
|
|
84
|
+
enigmapython/SettableReflector.py
|
|
78
85
|
enigmapython/Swappable.py
|
|
79
86
|
enigmapython/SwappablePlugboard.py
|
|
80
87
|
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.0",
|
|
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
|
-
)
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
class Utils:
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
@staticmethod
|
|
6
|
-
def find_divergence(str1, str2):
|
|
7
|
-
"""
|
|
8
|
-
Finds the index where two strings first diverge,
|
|
9
|
-
and the characters at that index in both strings.
|
|
10
|
-
|
|
11
|
-
Parameters:
|
|
12
|
-
str1 (str): The first string.
|
|
13
|
-
str2 (str): The second string.
|
|
14
|
-
|
|
15
|
-
Returns:
|
|
16
|
-
tuple: (index, char1, char2) where index is the position of divergence,
|
|
17
|
-
char1 is the character in str1 at the divergence,
|
|
18
|
-
and char2 is the character in str2 at the divergence.
|
|
19
|
-
If the strings only diverge in length, char1 and char2 will be None.
|
|
20
|
-
If the strings are identical, returns None.
|
|
21
|
-
"""
|
|
22
|
-
# Iterate through both strings up to the length of the shorter string
|
|
23
|
-
for i in range(min(len(str1), len(str2))):
|
|
24
|
-
if str1[i] != str2[i]:
|
|
25
|
-
return i, str1[i], str2[i] # Return the index and differing characters
|
|
26
|
-
|
|
27
|
-
# If the strings differ in length
|
|
28
|
-
if len(str1) != len(str2):
|
|
29
|
-
return min(len(str1), len(str2)), None, None
|
|
30
|
-
|
|
31
|
-
# If the strings are identical
|
|
32
|
-
return None
|
|
33
|
-
|
|
34
|
-
@staticmethod
|
|
35
|
-
def find_all_subclasses(cls):
|
|
36
|
-
return set(cls.__subclasses__()).union(
|
|
37
|
-
[s for c in cls.__subclasses__() for s in Utils.find_all_subclasses(c)])
|
|
38
|
-
|
|
39
|
-
@staticmethod
|
|
40
|
-
def swap_chars(string, ch1, ch2):
|
|
41
|
-
if ch1 == ch2: return string
|
|
42
|
-
str_list = []
|
|
43
|
-
for char in string:
|
|
44
|
-
if char == ch1:
|
|
45
|
-
str_list.append(ch2)
|
|
46
|
-
elif char == ch2:
|
|
47
|
-
str_list.append(ch1)
|
|
48
|
-
else:
|
|
49
|
-
str_list.append(char)
|
|
50
|
-
return ''.join(str_list)
|
|
51
|
-
|
|
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
|