Module szyfrow.pocket_enigma
The Pocket Enigma machine, a simple example that illustrates the mechanisms of the Enigma machine. See a review for more information about the machine.
Expand source code
"""The Pocket Enigma machine, a simple example that illustrates the mechanisms
of the Enigma machine. See [a review](http://www.savory.de/pocket_enigma.htm)
for more information about the machine.
"""
from szyfrow.support.utilities import *
from szyfrow.support.language_models import *
class PocketEnigma(object):
"""A pocket enigma machine
The wheel is internally represented as a 26-element list self.wheel_map,
where wheel_map[i] == j shows that the position i places on from the arrow
maps to the position j places on.
"""
def __init__(self, wheel=1, position='a'):
"""initialise the pocket enigma, including which wheel to use and the
starting position of the wheel.
The wheel is either 1 or 2 (the predefined wheels) or a list of letter
pairs.
The position is the letter pointed to by the arrow on the wheel.
>>> pe.wheel_map
[25, 4, 23, 10, 1, 7, 9, 5, 12, 6, 3, 17, 8, 14, 13, 21, 19, 11, 20, 16, 18, 15, 24, 2, 22, 0]
>>> pe.position
0
"""
self.wheel1 = [('a', 'z'), ('b', 'e'), ('c', 'x'), ('d', 'k'),
('f', 'h'), ('g', 'j'), ('i', 'm'), ('l', 'r'), ('n', 'o'),
('p', 'v'), ('q', 't'), ('s', 'u'), ('w', 'y')]
self.wheel2 = [('a', 'c'), ('b', 'd'), ('e', 'w'), ('f', 'i'),
('g', 'p'), ('h', 'm'), ('j', 'k'), ('l', 'n'), ('o', 'q'),
('r', 'z'), ('s', 'u'), ('t', 'v'), ('x', 'y')]
if wheel == 1:
self.make_wheel_map(self.wheel1)
elif wheel == 2:
self.make_wheel_map(self.wheel2)
else:
self.validate_wheel_spec(wheel)
self.make_wheel_map(wheel)
if position in string.ascii_lowercase:
self.position = pos(position)
else:
self.position = position
def make_wheel_map(self, wheel_spec):
"""Expands a wheel specification from a list of letter-letter pairs
into a full wheel_map.
>>> pe.make_wheel_map(pe.wheel2)
[2, 3, 0, 1, 22, 8, 15, 12, 5, 10, 9, 13, 7, 11, 16, 6, 14, 25, 20, 21, 18, 19, 4, 24, 23, 17]
"""
self.validate_wheel_spec(wheel_spec)
self.wheel_map = [0] * 26
for p in wheel_spec:
self.wheel_map[pos(p[0])] = pos(p[1])
self.wheel_map[pos(p[1])] = pos(p[0])
return self.wheel_map
def validate_wheel_spec(self, wheel_spec):
"""Validates that a wheel specificaiton will turn into a valid wheel
map.
>>> pe.validate_wheel_spec([])
Traceback (most recent call last):
...
ValueError: Wheel specification has 0 pairs, requires 13
>>> pe.validate_wheel_spec([('a', 'b', 'c')]*13)
Traceback (most recent call last):
...
ValueError: Not all mappings in wheel specificationhave two elements
>>> pe.validate_wheel_spec([('a', 'b')]*13)
Traceback (most recent call last):
...
ValueError: Wheel specification does not contain 26 letters
"""
if len(wheel_spec) != 13:
raise ValueError("Wheel specification has {} pairs, requires 13".
format(len(wheel_spec)))
for p in wheel_spec:
if len(p) != 2:
raise ValueError("Not all mappings in wheel specification"
"have two elements")
if len(set([p[0] for p in wheel_spec] +
[p[1] for p in wheel_spec])) != 26:
raise ValueError("Wheel specification does not contain 26 letters")
def encipher_letter(self, letter):
"""Enciphers a single letter, by advancing the wheel before looking up
the letter on the wheel.
>>> pe.set_position('f')
5
>>> pe.encipher_letter('k')
'h'
"""
self.advance()
return self.lookup(letter)
decipher_letter = encipher_letter
def lookup(self, letter):
"""Look up what a letter enciphers to, without turning the wheel.
>>> pe.set_position('f')
5
>>> cat([pe.lookup(l) for l in string.ascii_lowercase])
'udhbfejcpgmokrliwntsayqzvx'
>>> pe.lookup('A')
''
"""
if letter in string.ascii_lowercase:
return unpos(
(self.wheel_map[(pos(letter) - self.position) % 26] +
self.position))
else:
return ''
def advance(self):
"""Advances the wheel one position.
>>> pe.set_position('f')
5
>>> pe.advance()
6
"""
self.position = (self.position + 1) % 26
return self.position
def encipher(self, message, starting_position=None):
"""Enciphers a whole message.
>>> pe.set_position('f')
5
>>> pe.encipher('helloworld')
'kjsglcjoqc'
>>> pe.set_position('f')
5
>>> pe.encipher('kjsglcjoqc')
'helloworld'
>>> pe.encipher('helloworld', starting_position = 'x')
'egrekthnnf'
"""
if starting_position:
self.set_position(starting_position)
transformed = ''
for l in message:
transformed += self.encipher_letter(l)
return transformed
decipher = encipher
def set_position(self, position):
"""Sets the position of the wheel, by specifying the letter the arrow
points to.
>>> pe.set_position('a')
0
>>> pe.set_position('m')
12
>>> pe.set_position('z')
25
"""
self.position = pos(position)
return self.position
def pocket_enigma_break_by_crib(message, wheel_spec, crib, crib_position):
"""Break a pocket enigma using a crib (some plaintext that's expected to
be in a certain position). Returns a list of possible starting wheel
positions that could produce the crib.
>>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'h', 0)
['a', 'f', 'q']
>>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'he', 0)
['a']
>>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'll', 2)
['a']
>>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'l', 2)
['a']
>>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'l', 3)
['a', 'j', 'n']
>>> pocket_enigma_break_by_crib('aaaaa', 1, 'l', 3)
[]
"""
pe = PocketEnigma(wheel=wheel_spec)
possible_positions = []
for p in string.ascii_lowercase:
pe.set_position(p)
plaintext = pe.decipher(message)
if plaintext[crib_position:crib_position+len(crib)] == crib:
possible_positions += [p]
return possible_positions
if __name__ == "__main__":
import doctest
doctest.testmod(extraglobs={'pe': PocketEnigma(1, 'a')})
Functions
def cat(iterable, /)
-
Concatenate any number of strings.
The string whose method is called is inserted in between each given string. The result is returned as a new string.
Example: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'
def lcat(iterable, /)
-
Concatenate any number of strings.
The string whose method is called is inserted in between each given string. The result is returned as a new string.
Example: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'
def pocket_enigma_break_by_crib(message, wheel_spec, crib, crib_position)
-
Break a pocket enigma using a crib (some plaintext that's expected to be in a certain position). Returns a list of possible starting wheel positions that could produce the crib.
>>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'h', 0) ['a', 'f', 'q'] >>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'he', 0) ['a'] >>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'll', 2) ['a'] >>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'l', 2) ['a'] >>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'l', 3) ['a', 'j', 'n'] >>> pocket_enigma_break_by_crib('aaaaa', 1, 'l', 3) []
Expand source code
def pocket_enigma_break_by_crib(message, wheel_spec, crib, crib_position): """Break a pocket enigma using a crib (some plaintext that's expected to be in a certain position). Returns a list of possible starting wheel positions that could produce the crib. >>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'h', 0) ['a', 'f', 'q'] >>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'he', 0) ['a'] >>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'll', 2) ['a'] >>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'l', 2) ['a'] >>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'l', 3) ['a', 'j', 'n'] >>> pocket_enigma_break_by_crib('aaaaa', 1, 'l', 3) [] """ pe = PocketEnigma(wheel=wheel_spec) possible_positions = [] for p in string.ascii_lowercase: pe.set_position(p) plaintext = pe.decipher(message) if plaintext[crib_position:crib_position+len(crib)] == crib: possible_positions += [p] return possible_positions
def wcat(iterable, /)
-
Concatenate any number of strings.
The string whose method is called is inserted in between each given string. The result is returned as a new string.
Example: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'
Classes
class PocketEnigma (wheel=1, position='a')
-
A pocket enigma machine The wheel is internally represented as a 26-element list self.wheel_map, where wheel_map[i] == j shows that the position i places on from the arrow maps to the position j places on.
initialise the pocket enigma, including which wheel to use and the starting position of the wheel.
The wheel is either 1 or 2 (the predefined wheels) or a list of letter pairs.
The position is the letter pointed to by the arrow on the wheel.
>>> pe.wheel_map [25, 4, 23, 10, 1, 7, 9, 5, 12, 6, 3, 17, 8, 14, 13, 21, 19, 11, 20, 16, 18, 15, 24, 2, 22, 0] >>> pe.position 0
Expand source code
class PocketEnigma(object): """A pocket enigma machine The wheel is internally represented as a 26-element list self.wheel_map, where wheel_map[i] == j shows that the position i places on from the arrow maps to the position j places on. """ def __init__(self, wheel=1, position='a'): """initialise the pocket enigma, including which wheel to use and the starting position of the wheel. The wheel is either 1 or 2 (the predefined wheels) or a list of letter pairs. The position is the letter pointed to by the arrow on the wheel. >>> pe.wheel_map [25, 4, 23, 10, 1, 7, 9, 5, 12, 6, 3, 17, 8, 14, 13, 21, 19, 11, 20, 16, 18, 15, 24, 2, 22, 0] >>> pe.position 0 """ self.wheel1 = [('a', 'z'), ('b', 'e'), ('c', 'x'), ('d', 'k'), ('f', 'h'), ('g', 'j'), ('i', 'm'), ('l', 'r'), ('n', 'o'), ('p', 'v'), ('q', 't'), ('s', 'u'), ('w', 'y')] self.wheel2 = [('a', 'c'), ('b', 'd'), ('e', 'w'), ('f', 'i'), ('g', 'p'), ('h', 'm'), ('j', 'k'), ('l', 'n'), ('o', 'q'), ('r', 'z'), ('s', 'u'), ('t', 'v'), ('x', 'y')] if wheel == 1: self.make_wheel_map(self.wheel1) elif wheel == 2: self.make_wheel_map(self.wheel2) else: self.validate_wheel_spec(wheel) self.make_wheel_map(wheel) if position in string.ascii_lowercase: self.position = pos(position) else: self.position = position def make_wheel_map(self, wheel_spec): """Expands a wheel specification from a list of letter-letter pairs into a full wheel_map. >>> pe.make_wheel_map(pe.wheel2) [2, 3, 0, 1, 22, 8, 15, 12, 5, 10, 9, 13, 7, 11, 16, 6, 14, 25, 20, 21, 18, 19, 4, 24, 23, 17] """ self.validate_wheel_spec(wheel_spec) self.wheel_map = [0] * 26 for p in wheel_spec: self.wheel_map[pos(p[0])] = pos(p[1]) self.wheel_map[pos(p[1])] = pos(p[0]) return self.wheel_map def validate_wheel_spec(self, wheel_spec): """Validates that a wheel specificaiton will turn into a valid wheel map. >>> pe.validate_wheel_spec([]) Traceback (most recent call last): ... ValueError: Wheel specification has 0 pairs, requires 13 >>> pe.validate_wheel_spec([('a', 'b', 'c')]*13) Traceback (most recent call last): ... ValueError: Not all mappings in wheel specificationhave two elements >>> pe.validate_wheel_spec([('a', 'b')]*13) Traceback (most recent call last): ... ValueError: Wheel specification does not contain 26 letters """ if len(wheel_spec) != 13: raise ValueError("Wheel specification has {} pairs, requires 13". format(len(wheel_spec))) for p in wheel_spec: if len(p) != 2: raise ValueError("Not all mappings in wheel specification" "have two elements") if len(set([p[0] for p in wheel_spec] + [p[1] for p in wheel_spec])) != 26: raise ValueError("Wheel specification does not contain 26 letters") def encipher_letter(self, letter): """Enciphers a single letter, by advancing the wheel before looking up the letter on the wheel. >>> pe.set_position('f') 5 >>> pe.encipher_letter('k') 'h' """ self.advance() return self.lookup(letter) decipher_letter = encipher_letter def lookup(self, letter): """Look up what a letter enciphers to, without turning the wheel. >>> pe.set_position('f') 5 >>> cat([pe.lookup(l) for l in string.ascii_lowercase]) 'udhbfejcpgmokrliwntsayqzvx' >>> pe.lookup('A') '' """ if letter in string.ascii_lowercase: return unpos( (self.wheel_map[(pos(letter) - self.position) % 26] + self.position)) else: return '' def advance(self): """Advances the wheel one position. >>> pe.set_position('f') 5 >>> pe.advance() 6 """ self.position = (self.position + 1) % 26 return self.position def encipher(self, message, starting_position=None): """Enciphers a whole message. >>> pe.set_position('f') 5 >>> pe.encipher('helloworld') 'kjsglcjoqc' >>> pe.set_position('f') 5 >>> pe.encipher('kjsglcjoqc') 'helloworld' >>> pe.encipher('helloworld', starting_position = 'x') 'egrekthnnf' """ if starting_position: self.set_position(starting_position) transformed = '' for l in message: transformed += self.encipher_letter(l) return transformed decipher = encipher def set_position(self, position): """Sets the position of the wheel, by specifying the letter the arrow points to. >>> pe.set_position('a') 0 >>> pe.set_position('m') 12 >>> pe.set_position('z') 25 """ self.position = pos(position) return self.position
Methods
def advance(self)
-
Advances the wheel one position.
>>> pe.set_position('f') 5 >>> pe.advance() 6
Expand source code
def advance(self): """Advances the wheel one position. >>> pe.set_position('f') 5 >>> pe.advance() 6 """ self.position = (self.position + 1) % 26 return self.position
def decipher(self, message, starting_position=None)
-
Enciphers a whole message.
>>> pe.set_position('f') 5 >>> pe.encipher('helloworld') 'kjsglcjoqc' >>> pe.set_position('f') 5 >>> pe.encipher('kjsglcjoqc') 'helloworld' >>> pe.encipher('helloworld', starting_position = 'x') 'egrekthnnf'
Expand source code
def encipher(self, message, starting_position=None): """Enciphers a whole message. >>> pe.set_position('f') 5 >>> pe.encipher('helloworld') 'kjsglcjoqc' >>> pe.set_position('f') 5 >>> pe.encipher('kjsglcjoqc') 'helloworld' >>> pe.encipher('helloworld', starting_position = 'x') 'egrekthnnf' """ if starting_position: self.set_position(starting_position) transformed = '' for l in message: transformed += self.encipher_letter(l) return transformed
def decipher_letter(self, letter)
-
Enciphers a single letter, by advancing the wheel before looking up the letter on the wheel.
>>> pe.set_position('f') 5 >>> pe.encipher_letter('k') 'h'
Expand source code
def encipher_letter(self, letter): """Enciphers a single letter, by advancing the wheel before looking up the letter on the wheel. >>> pe.set_position('f') 5 >>> pe.encipher_letter('k') 'h' """ self.advance() return self.lookup(letter)
def encipher(self, message, starting_position=None)
-
Enciphers a whole message.
>>> pe.set_position('f') 5 >>> pe.encipher('helloworld') 'kjsglcjoqc' >>> pe.set_position('f') 5 >>> pe.encipher('kjsglcjoqc') 'helloworld' >>> pe.encipher('helloworld', starting_position = 'x') 'egrekthnnf'
Expand source code
def encipher(self, message, starting_position=None): """Enciphers a whole message. >>> pe.set_position('f') 5 >>> pe.encipher('helloworld') 'kjsglcjoqc' >>> pe.set_position('f') 5 >>> pe.encipher('kjsglcjoqc') 'helloworld' >>> pe.encipher('helloworld', starting_position = 'x') 'egrekthnnf' """ if starting_position: self.set_position(starting_position) transformed = '' for l in message: transformed += self.encipher_letter(l) return transformed
def encipher_letter(self, letter)
-
Enciphers a single letter, by advancing the wheel before looking up the letter on the wheel.
>>> pe.set_position('f') 5 >>> pe.encipher_letter('k') 'h'
Expand source code
def encipher_letter(self, letter): """Enciphers a single letter, by advancing the wheel before looking up the letter on the wheel. >>> pe.set_position('f') 5 >>> pe.encipher_letter('k') 'h' """ self.advance() return self.lookup(letter)
def lookup(self, letter)
-
Look up what a letter enciphers to, without turning the wheel.
>>> pe.set_position('f') 5 >>> cat([pe.lookup(l) for l in string.ascii_lowercase]) 'udhbfejcpgmokrliwntsayqzvx' >>> pe.lookup('A') ''
Expand source code
def lookup(self, letter): """Look up what a letter enciphers to, without turning the wheel. >>> pe.set_position('f') 5 >>> cat([pe.lookup(l) for l in string.ascii_lowercase]) 'udhbfejcpgmokrliwntsayqzvx' >>> pe.lookup('A') '' """ if letter in string.ascii_lowercase: return unpos( (self.wheel_map[(pos(letter) - self.position) % 26] + self.position)) else: return ''
def make_wheel_map(self, wheel_spec)
-
Expands a wheel specification from a list of letter-letter pairs into a full wheel_map.
>>> pe.make_wheel_map(pe.wheel2) [2, 3, 0, 1, 22, 8, 15, 12, 5, 10, 9, 13, 7, 11, 16, 6, 14, 25, 20, 21, 18, 19, 4, 24, 23, 17]
Expand source code
def make_wheel_map(self, wheel_spec): """Expands a wheel specification from a list of letter-letter pairs into a full wheel_map. >>> pe.make_wheel_map(pe.wheel2) [2, 3, 0, 1, 22, 8, 15, 12, 5, 10, 9, 13, 7, 11, 16, 6, 14, 25, 20, 21, 18, 19, 4, 24, 23, 17] """ self.validate_wheel_spec(wheel_spec) self.wheel_map = [0] * 26 for p in wheel_spec: self.wheel_map[pos(p[0])] = pos(p[1]) self.wheel_map[pos(p[1])] = pos(p[0]) return self.wheel_map
def set_position(self, position)
-
Sets the position of the wheel, by specifying the letter the arrow points to.
>>> pe.set_position('a') 0 >>> pe.set_position('m') 12 >>> pe.set_position('z') 25
Expand source code
def set_position(self, position): """Sets the position of the wheel, by specifying the letter the arrow points to. >>> pe.set_position('a') 0 >>> pe.set_position('m') 12 >>> pe.set_position('z') 25 """ self.position = pos(position) return self.position
def validate_wheel_spec(self, wheel_spec)
-
Validates that a wheel specificaiton will turn into a valid wheel map.
>>> pe.validate_wheel_spec([]) Traceback (most recent call last): ... ValueError: Wheel specification has 0 pairs, requires 13 >>> pe.validate_wheel_spec([('a', 'b', 'c')]*13) Traceback (most recent call last): ... ValueError: Not all mappings in wheel specificationhave two elements >>> pe.validate_wheel_spec([('a', 'b')]*13) Traceback (most recent call last): ... ValueError: Wheel specification does not contain 26 letters
Expand source code
def validate_wheel_spec(self, wheel_spec): """Validates that a wheel specificaiton will turn into a valid wheel map. >>> pe.validate_wheel_spec([]) Traceback (most recent call last): ... ValueError: Wheel specification has 0 pairs, requires 13 >>> pe.validate_wheel_spec([('a', 'b', 'c')]*13) Traceback (most recent call last): ... ValueError: Not all mappings in wheel specificationhave two elements >>> pe.validate_wheel_spec([('a', 'b')]*13) Traceback (most recent call last): ... ValueError: Wheel specification does not contain 26 letters """ if len(wheel_spec) != 13: raise ValueError("Wheel specification has {} pairs, requires 13". format(len(wheel_spec))) for p in wheel_spec: if len(p) != 2: raise ValueError("Not all mappings in wheel specification" "have two elements") if len(set([p[0] for p in wheel_spec] + [p[1] for p in wheel_spec])) != 26: raise ValueError("Wheel specification does not contain 26 letters")