Posts: 15
Threads: 1
Joined: Nov 2017
(Nov-22-2017, 08:32 PM)Larz60+ Wrote: Kimkug, have you found anything like this?
Yes indeed I ave heard of interchangeable reflectors too. I am not sure from when on they were used, but only later on in the war.
I have nethertheless not heard of any key that was given.. I know, that each post, having an enigma machine to trancsport messages, had a sheet, where were written all the settings for the machine (of the day), so there was no need to communicate any sort of key?
Anyways, hank you a lot for your help! If possible, could you insert some comments, just in case I need to simplify the program to show others, where to remove for example the multiple triggers in each rotor (if you have some in the program), and other small details of the machine? The only purpose I have for it, is that it should be ready for demonstration purposes (encrypting and decrpyting should therefor work). I thank you a lot for you time and work!
Posts: 12,031
Threads: 485
Joined: Sep 2016
I think I got the link to this: http://www.ilord.com/enigma.html from your site.
there is a section for downloading manuals.
The Wikipedia page also has some useful links.
This whole think is going to get me on a crypto roll, probably for the next six months.
I did a lot in the past with PGP when it first emerged, and had my 'spy' wheels when I
was a kid (also had micron camera and other geek toys).
Posts: 1,298
Threads: 38
Joined: Sep 2016
(Nov-22-2017, 09:06 PM)Larz60+ Wrote: This whole think is going to get me on a crypto roll
Was thinking last night, if I had a 3D printer for the rotor, rotor rings and reflectors, I could build my own replica!! Had to slap myself back to reality.
If it ain't broke, I just haven't gotten to it yet.
OS: Windows 10, openSuse 42.3, freeBSD 11, Raspian "Stretch"
Python 3.6.5, IDE: PyCharm 2018 Community Edition
Posts: 12,031
Threads: 485
Joined: Sep 2016
Nov-22-2017, 10:41 PM
(This post was last modified: Nov-22-2017, 10:41 PM by Larz60+.)
My brother is a mechanical engineer.
He makes all kinds of things. Take a look at this Harley 1/4 scale
Knucklehead engine which is built to run: https://www.youtube.com/watch?v=lZutS12Q3Qs
Posts: 12,031
Threads: 485
Joined: Sep 2016
Nov-24-2017, 09:18 PM
(This post was last modified: Nov-24-2017, 09:18 PM by Larz60+.)
I have the incrementor working properly, (at least in the forward direction, bucause I'm not sure how to use the notches
on the reverse trip.
What I'm still not sure of is the way the rotor's rotation affects the cipher output,
because following Tony Sands example, it doesn't seem to have an effect. In addition,
I'm sure I've got the rotation increment correct in the forward direction, but still not clear on the reverse direction action.
New code Incrementor.py with full test (built into script) is now on github:
Testing against the Tony Sands example, here are the results:
First the code:
import json
import EnigmaPaths
class Incrementor:
def __init__(self, rinfo, start_index=0):
"""
Initialize - load rotor info, set starting indexes, get cipher
:param rinfo:
"""
self.epath = EnigmaPaths.EnigmaPaths()
with self.epath.enigma_info.open() as f:
self.init_data = json.load(f)
self.cipher = rinfo['cipher']
self.notch = rinfo['notches'][0]
self.alpha = self.init_data['unencoded']
self.index = start_index
self.max_index = len(self.cipher) - 1
def next_index(self):
self.index += 1
if self.index > self.max_index:
self.index = 0
return self.index
def get_next_item(self, alpha_char, direction='f', preincrement=False):
index = self.index
if preincrement:
index = self.next_index()
if direction == 'f':
aidx = self.alpha.index(alpha_char)
item = self.cipher[aidx]
else:
cidx = self.cipher.index(alpha_char)
item = self.alpha[cidx]
advance = (self.alpha[index] == self.notch)
if not preincrement:
index = self.next_index()
return item, advance
def set_initial_index(self, index):
if index >= len(self.cipher):
print(f'index out of range, valid range is 0 - {self.max_index}')
else:
self.index = index
def query_cipher(self):
return self.cipher
def query_index(self):
return self.index
def query_notch(self):
return self.notch
def testit():
# Order if progression is from right to left (rotors)
# Assume 'B' selected for reflector
# Our test letter.
test_letter = 'G'
# Setup right rotor(start index = 2, set by set_initial_index):
rinfo = {'name': 'rotor3', 'cipher': 'BDFHJLCPRTXVZNYEIWGAKMUSQO', 'notches': ['W']}
r_incrementor = Incrementor(rinfo)
# Get reflector here (needed init_data to be visible
test_reflector = r_incrementor.init_data['reflector_B']
r_incrementor.set_initial_index(2)
print(f'\nright alpha: {r_incrementor.alpha}')
print(f'right cipher: {r_incrementor.query_cipher()}')
print(f'right index: {r_incrementor.query_index()}')
print(f'right notch: {r_incrementor.query_notch()}')
# Setup middle rotor (start index = 4 'S'):
rinfo = {'name': 'rotor2', 'cipher': 'AJDKSIRUXBLHWTMCQGZNPYFVOE', 'notches': ['F']}
m_incrementor = Incrementor(rinfo, start_index=4)
print(f'\nmiddle alpha: {m_incrementor.alpha}')
print(f'middle cipher: {m_incrementor.query_cipher()}')
print(f'middle index: {m_incrementor.query_index()}')
print(f'middle notch: {m_incrementor.query_notch()}')
# Setup left rotor (start index = 16):
rinfo = {'name': 'rotor1', 'cipher': 'EKMFLGDQVZNTOWYHXUSPAIBRCJ', 'notches': ['R']}
l_incrementor = Incrementor(rinfo, start_index=16)
print(f'\nleft alpha: {l_incrementor.alpha}')
print(f'left cipher: {l_incrementor.query_cipher()}')
print(f'left index: {l_incrementor.query_index()}')
print(f'left notch: {l_incrementor.query_notch()}')
# Test one letter through entire process
# First get right rotor cipher and advance rotor
ritem, advance = r_incrementor.get_next_item(test_letter, direction='f')
print(f'\nright rotor cipher: in: {test_letter}, out: {ritem}, advance: {advance}')
# if advance is True increment middle rotor by 1
# ignore cipher, in each case but check advance again
if advance:
# This means slot was encountered on middle advance, so
# have to advance left!
mitem, advance = m_incrementor.get_next_item(ritem, direction='f')
if advance:
# This means middle slot was encountered on advance
litem, advance = l_incrementor.get_next_item(mitem, direction='f')
# Get middle rotor cipher and advance rotor
mitem, advance = m_incrementor.get_next_item(ritem, direction='f')
print(f'\nmiddle rotor cipher: in: {ritem}, out: {mitem}, advance: {advance}')
if advance:
# This means middle slot was encountered on advance
litem, advance = l_incrementor.get_next_item(mitem, direction='f')
litem, advance = l_incrementor.get_next_item(mitem, direction='f')
print(f'\nleft rotor cipher: in: {mitem} out: {litem}, advance: {advance}')
# Read up on what to do if advance = true, is the middle
# advanced??
# run through reflector
aidx = l_incrementor.alpha.index(litem)
refl_item = test_reflector[aidx]
print(f'\nreflector output: in: {litem}, out: {refl_item}')
# reverse trip
print('Return trip')
litem, advance = l_incrementor.get_next_item(refl_item, direction='r')
print(f'\nleft rotor cipher: in: {refl_item}, out: {litem}, advance: {advance}')
# Read up on what to do if advance = true, is the middle
# advanced??
# Get middle rotor cipher and advance rotor
mitem, advance = m_incrementor.get_next_item(litem, direction='r')
print(f'\nmiddle rotor cipher: in: {litem}, out: {mitem}, advance: {advance}')
ritem, advance = r_incrementor.get_next_item(mitem, direction='r')
print(f'\nright rotor cipher: in: {mitem}, out: {ritem}, advance: {advance}')
print(f'\n\nletter in: {test_letter}, letter out: {ritem}')
if __name__ == '__main__':
testit() and built in test results:
Output: right alpha: ABCDEFGHIJKLMNOPQRSTUVWXYZ
right cipher: BDFHJLCPRTXVZNYEIWGAKMUSQO
right index: 2
right notch: W
middle alpha: ABCDEFGHIJKLMNOPQRSTUVWXYZ
middle cipher: AJDKSIRUXBLHWTMCQGZNPYFVOE
middle index: 4
middle notch: F
left alpha: ABCDEFGHIJKLMNOPQRSTUVWXYZ
left cipher: EKMFLGDQVZNTOWYHXUSPAIBRCJ
left index: 16
left notch: R
right rotor cipher: in: G, out: C, advance: False
middle rotor cipher: in: C, out: D, advance: False
left rotor cipher: in: D out: F, advance: False
reflector output: in: F, out: S
Return trip
left rotor cipher: in: S, out: S, advance: True
middle rotor cipher: in: S, out: E, advance: True
right rotor cipher: in: E, out: P, advance: False
letter in: G, letter out: P
Posts: 12,031
Threads: 485
Joined: Sep 2016
I Think the best explanation for the Enigma technical operation is in this document:
https://www.nsa.gov/about/cryptologic-he...cipher.pdf
Posts: 12,031
Threads: 485
Joined: Sep 2016
Nov-25-2017, 05:09 AM
(This post was last modified: Nov-25-2017, 05:09 AM by Larz60+.)
I found an online emulator. Wish I had of seen this before I started!
http://enigma.louisedade.co.uk/enigma.html
By the way, the 'key' referred to the initial settings of the wheels, ex: 'AQT'
And the plugboard.
Posts: 12,031
Threads: 485
Joined: Sep 2016
Nov-26-2017, 11:31 AM
(This post was last modified: Nov-26-2017, 11:31 AM by Larz60+.)
Here are the complete details of how a key sheet worked:
Output: Table Source: http://users.telenet.be/d.rijmenants/en/enigmaproc.htm
The Heer and Luftwaffe Procedures
To obtain secure communications, the German Heer (Army) and Luftwaffe
(Air Force) used standard procedures to transmit and receive messages.
For a message to be correctly encrypted and decrypted, both the sender
and receiver needed to set up their Enigma in exactly the same way. These
settings were distributed in key sheets. For reasons of security,
different parts of the armed forces had their own network, with different
key sheets and with a network having its own codename.
Each key sheet contained the following information:
Walzenlage: Choice and order of wheels
Ringstellung: The ringsetting, the position of the rotor wiring,
relative to the alphabet rings
Steckerverbindungen: The plug connections on the plugboard
Kenngruppen: Groups to identify the key to the receiver.
The key sheets were distributed on beforehand, and contained the basic
settings for a whole month, per day. In general, the key sheets were in
the custody of an officer, responsible for setting up the machine rotors
and ringsettings. After setup, he could lock the machine front panel
with a key. The operator could only select the rotor start position.
Armee Stabs Maschinenschlussel Nr. 28
(Army Staffs Machine Key No. 28)
---------------------------------------------------------------------------------------
Tag | Walzenlage | Ringstellung | Steckerverbindungen | Kenngruppen
---------------------------------------------------------------------------------------
31 | IV V I | 21 15 16 | KL IT FQ HY XC NP VZ JB SE OG | JKM OGO NCJ GLP
30 | IV II III | 26 14 11 | ZN YO QB ER DK XU GP TV SJ LM | INO UDL NAM LAX
29 | II V IV | 19 09 24 | ZU HL CQ WM OA PY EB TR DN VI | NCI OID YHP NIP
28 | IV III I | 03 04 22 | YT BX CV ZN UD IR SJ HW GA KQ | ZQJ HLG XKY EBT
27 | I II V | 06 22 14 | PO ML IU KJ NH YT GB VF RE DC | EXS TGY IKJ LOP
---------------------------------------------------------------------------------------
o To identify the key that was used for a particular message, the operator had to insert
a five letter group called Buchstabenkenngruppe (letter identification group) as the
first group of the message. The Buchstabenkenngruppe is composed of two randomly selected
letters and one of the four possible three-letter Kenngruppen at the key sheet for that
day. If we take day 31 from the Army Staff key 28 (image above), we see the Kenngruppen
JKM, OGI, NCJ and GLP. In this case, some examples of a correct Buchstabenkenngruppe are
FDJKM, KVOGI or QNNCJ. This five letter group at the start of the message should not be
encrypted with the rest of the message! If a message was devided into several parts, the
operator had to insert another Buchstabenkenngruppe for each part of the message. When
counting the letters for the message header, the five letters of the Buchstabenkenngruppe
must be included. The receiving operator immediately recognized which key was to be
applied by looking at the last three letters of the first group.
o The setting of the machine was typically valid for one day. Using the same settings for a
large number of messages would increase the statistical amount of data to break a
particular key. Therefore, each message was sent with a different startposition of the
Enigma rotors, randomly selected by the operator. This was called the Spruchschlüssel
or message key.
Before 1940, the German military used the daily key and startposition, according to the key
sheet. The operator selected a random message key. This message key was encoded twice, to
exclude errors. As example, the trigram GHK is encoded twice, resulting in XMC FZQ. Next,
the operator moved the rotors to the message key GHK and encoded the message. The two
trigrams, being the encoded message key, were transmitted, together with the message. The
receiver sets his machine on the start position, as described in the codebook, and decodes
the trigrams XMC FZQ back into the GHK message key. Next, he sets the message key GHK as
start position on his machine, to continue decoding the rest of the message. However, this
procedure was actually a security flaw. The message key is encoded twice, resulting in a
relation between first and fourth, second and fifth, and third and sixth character.
Moreover, many message keys on a particular day would have the same setup and startpositions.
This security problem enabled the Polish Cipher Bureau to break the pre-war Enigma messages.
However, German cryptologists were aware of the security flaw and from 1940 on, the Wehrmacht
changed the message key procedures to increase security.
Wehrmacht radio operators now selected for each message a new randomly chosen start position
or Grundstellung, let's say WZA, and random message key or Spruchschlüssel, let's say SXT. He
moved the rotors to the random startposition WZA, and encoded the random message key SXT. Let
us presume that the result was UHL. He sets up the message key SXT as startposition and
encodes the message. Next, he transmits the random start position WZA, the encoded message key
UHL and the message. The receiver sets up the start position according the first trigram WZA,
and decodes the second trigram UHL to obtain the message key SXT. Next, he uses the message
key SXT as startposition to decode the actual message. If a message was devided into several
parts, the operator had to insert a new startposition and message key for each part of the
message.
Example of a typical Wehrmacht message:
1230 = 3tle = 1tl = 250 = WZA UHL =
FDJKM LDAHH YEOEF PTWYB LENDP
MKOXL DFAMU DWIJD XRJZY DFRIO
MFTEV KTGUY DDZED TPOQX FDRIU
CCBFM MQWYE FIPUL WSXHG YHJZE
AOFDU FUTEC VVBDP OLZLG DEJTI
HGYER DCXCV BHSEE TTKJK XAAQU
GTTUO FCXZH IDREF TGHSZ DERFG
EDZZS ERDET RFGTT RREOM MJMED
EDDER FTGRE UUHKD DLEFG FGREZ
ZZSEU YYRGD EDFED HJUIK FXNVB
The message was created at 12h30, consists of three parts (3 teile), of which this is the
first, and contains 250 characters (Buchstabenkenngruppe included). WZA is the startposition
(Grundstellung) to decipher the encrypted message key (Spruchschlüssel) UHL. The
Buchstabenkenngruppe FDJKM shows that the key that was used is the one with Kenngruppe JKM.
Example for decrypt:
U6Z DE C 1510 = 49 = EHZ TBS =
TVEXS QBLTW LDAHH YEOEF
PTWYB LENDP MKOXL DFAMU
DWIJD XRJZ=
To decrypt the message we proceed as follows:
• Select the Wehrmacht Enigma I with B reflector.
• Select the rotors, adjust their ring setting and set the plugs according to key sheet day 27
• Set the rotor start positions to EHZ, the first trigram of the message
• Type in the second trigram TBS to retrieve the original message key. The result should be XWB
• Set the decrypted message key XWB as start position for the three rotors.
• Now decrypt the actual message, but make sure to skip the key identification group TVEXS.
Note: in the pre-war Wehrmacht procedure, each message key was encrypted twice (to exclude errors)
by a fixed secret basic position, valid for the whole day. For instance, with basic setting ABC,
the message key XYZ was keyed in twice, resulting in JKL MNO. Only the double encrypted message key
JKL MNO was sent along with the message. However, this created a mathematical relation between J
and M, K and N, and L and O, a flaw that was exploited by the Polish codebreakers. German
cryptologists understood this flaw and dropped the double encrypted message key in 1939, replacing
it with a random basic position, sent along with a once encrypted message key.
Posts: 1,298
Threads: 38
Joined: Sep 2016
Very good explanation of the actual usage. Clears up for me the use of the "Kenngruppen". Thanks for posting.
If it ain't broke, I just haven't gotten to it yet.
OS: Windows 10, openSuse 42.3, freeBSD 11, Raspian "Stretch"
Python 3.6.5, IDE: PyCharm 2018 Community Edition
Posts: 15
Threads: 1
Joined: Nov 2017
Wow, I'm so very impressed right now. I did not know much about the information about the "key" you sent, and this will be a big help!
Just a question, how far from completition is the program at the moment?
I still have to thank you in every message for the work you are taking on! So thamk you a lot, and it isn't too urgent that it is done, it just sounded you were near to done ;)
|