X-Git-Url: https://git.njae.me.uk/?a=blobdiff_plain;f=enigma.py;h=89758f532087985078041473006ce3d6510e3933;hb=6399b876a6d50d11ad318ab0fd0db74e03168ed8;hp=72f480792170725c0b59462781d338961c534510;hpb=628c83045960e5d94ea55fe90437b465a860d4c7;p=cipher-tools.git diff --git a/enigma.py b/enigma.py index 72f4807..89758f5 100644 --- a/enigma.py +++ b/enigma.py @@ -16,6 +16,21 @@ import string import collections import multiprocessing import itertools +import logging + +logger = logging.getLogger('engima') +logger.setLevel(logging.WARNING) +# logger.setLevel(logging.INFO) +# logger.setLevel(logging.DEBUG) + +# create the logging file handler +fh = logging.FileHandler("enigma.log") +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +fh.setFormatter(formatter) + +# add handler to logger object +logger.addHandler(fh) + # Some convenience functions @@ -45,14 +60,14 @@ wheel_viii_spec = 'fkqhtlxocbjspdzramewniuygv' beta_wheel_spec = 'leyjvcnixwpbqmdrtakzgfuhos' gamma_wheel_spec = 'fsokanuerhmbtiycwlqpzxvgjd' -wheel_i_pegs = ['q'] -wheel_ii_pegs = ['e'] -wheel_iii_pegs = ['v'] -wheel_iv_pegs = ['j'] -wheel_v_pegs = ['z'] -wheel_vi_pegs = ['z', 'm'] -wheel_vii_pegs = ['z', 'm'] -wheel_viii_pegs = ['z', 'm'] +wheel_i_notches = ['q'] +wheel_ii_notches = ['e'] +wheel_iii_notches = ['v'] +wheel_iv_notches = ['j'] +wheel_v_notches = ['z'] +wheel_vi_notches = ['z', 'm'] +wheel_vii_notches = ['z', 'm'] +wheel_viii_notches = ['z', 'm'] reflector_b_spec = 'ay br cu dh eq fs gl ip jx kn mo tz vw' reflector_c_spec = 'af bv cp dj ei go hy kr lz mx nw tq su' @@ -63,24 +78,6 @@ class LetterTransformer(object): """A generic substitution cipher, that has different transforms in the forward and backward directions. It requires that the transforms for all letters by provided. - - >>> lt = LetterTransformer([('z', 'a')] + [(l, string.ascii_lowercase[i+1]) \ - for i, l in enumerate(string.ascii_lowercase[:-1])], \ - raw_transform = True) - >>> lt.forward_map - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0] - >>> lt.backward_map - [25, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24] - - >>> lt = LetterTransformer(cat(collections.OrderedDict.fromkeys('zyxwc' + string.ascii_lowercase))) - >>> lt.forward_map - [25, 24, 23, 22, 2, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] - >>> lt.backward_map - [5, 6, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 3, 2, 1, 0] - >>> cat(lt.forward(l) for l in string.ascii_lowercase) - 'zyxwcabdefghijklmnopqrstuv' - >>> cat(lt.backward(l) for l in string.ascii_lowercase) - 'fgehijklmnopqrstuvwxyzdcba' """ def __init__(self, specification, raw_transform=False): if raw_transform: @@ -136,16 +133,6 @@ class Plugboard(LetterTransformer): """A plugboard, a type of letter transformer where forward and backward transforms are the same. If a letter isn't explicitly transformed, it is kept as it is. - - >>> pb = Plugboard('ua pf rq so ni ey bg hl tx zj'.upper()) - >>> pb.forward_map - [20, 6, 2, 3, 24, 15, 1, 11, 13, 25, 10, 7, 12, 8, 18, 5, 17, 16, 14, 23, 0, 21, 22, 19, 4, 9] - >>> pb.forward_map == pb.backward_map - True - >>> cat(pb.forward(l) for l in string.ascii_lowercase) - 'ugcdypblnzkhmisfrqoxavwtej' - >>> cat(pb.backward(l) for l in string.ascii_lowercase) - 'ugcdypblnzkhmisfrqoxavwtej' """ def parse_specification(self, specification): return [tuple(clean(p)) for p in specification.split()] @@ -170,16 +157,6 @@ class Plugboard(LetterTransformer): class Reflector(Plugboard): """A reflector is a plugboard that requires 13 transforms. - - >>> reflector_b = Reflector(reflector_b_spec) - >>> reflector_b.forward_map == reflector_b.backward_map - True - >>> reflector_b.forward_map - [24, 17, 20, 7, 16, 18, 11, 3, 15, 23, 13, 6, 14, 10, 12, 8, 4, 1, 5, 25, 2, 22, 21, 9, 0, 19] - >>> cat(reflector_b.forward(l) for l in string.ascii_lowercase) - 'yruhqsldpxngokmiebfzcwvjat' - >>> cat(reflector_b.backward(l) for l in string.ascii_lowercase) - 'yruhqsldpxngokmiebfzcwvjat' """ def validate_transform(self, transform): if len(transform) != 13: @@ -208,52 +185,6 @@ class SimpleWheel(LetterTransformer): Letter inputs and outputs are given relative to the frame holding the wheel, so if the wheel is advanced three places, an input of 'p' will enter the wheel on the position under the wheel's 'q' label. - - >>> rotor_1_transform = list(zip(string.ascii_lowercase, 'EKMFLGDQVZNTOWYHXUSPAIBRCJ'.lower())) - >>> wheel_1 = SimpleWheel(rotor_1_transform, raw_transform=True) - >>> cat(wheel_1.forward(l) for l in string.ascii_lowercase) - 'ekmflgdqvzntowyhxuspaibrcj' - >>> cat(wheel_1.backward(l) for l in string.ascii_lowercase) - 'uwygadfpvzbeckmthxslrinqoj' - - - >>> wheel_2 = SimpleWheel(wheel_ii_spec) - >>> cat(wheel_2.forward(l) for l in string.ascii_lowercase) - 'ajdksiruxblhwtmcqgznpyfvoe' - >>> cat(wheel_2.backward(l) for l in string.ascii_lowercase) - 'ajpczwrlfbdkotyuqgenhxmivs' - - >>> wheel_3 = SimpleWheel(wheel_iii_spec) - >>> wheel_3.set_position('a') - >>> wheel_3.advance() - >>> cat(wheel_3.forward(l) for l in string.ascii_lowercase) - 'cegikboqswuymxdhvfzjltrpna' - >>> cat(wheel_3.backward(l) for l in string.ascii_lowercase) - 'zfaobrcpdteumygxhwivkqjnls' - >>> wheel_3.position - 1 - >>> wheel_3.position_l - 'b' - - >>> for _ in range(24): wheel_3.advance() - >>> wheel_3.position - 25 - >>> wheel_3.position_l - 'z' - >>> cat(wheel_3.forward(l) for l in string.ascii_lowercase) - 'pcegikmdqsuywaozfjxhblnvtr' - >>> cat(wheel_3.backward(l) for l in string.ascii_lowercase) - 'nubhcqdterfvgwoaizjykxmslp' - - >>> wheel_3.advance() - >>> wheel_3.position - 0 - >>> wheel_3.position_l - 'a' - >>> cat(wheel_3.forward(l) for l in string.ascii_lowercase) - 'bdfhjlcprtxvznyeiwgakmusqo' - >>> cat(wheel_3.backward(l) for l in string.ascii_lowercase) - 'tagbpcsdqeufvnzhyixjwlrkom' """ def __init__(self, transform, position='a', raw_transform=False): super(SimpleWheel, self).__init__(transform, raw_transform) @@ -266,7 +197,11 @@ class SimpleWheel(LetterTransformer): return object.__getattribute__(self, name) def set_position(self, position): - self.position = ord(position) - ord('a') + if isinstance(position, str): + # self.position = ord(position) - ord('a') + self.position = pos(position) + else: + self.position = position def forward(self, letter): if letter in string.ascii_lowercase: @@ -288,7 +223,7 @@ class SimpleWheel(LetterTransformer): class Wheel(SimpleWheel): """A wheel with a movable ring. - The ring holds the letters and the pegs that turn other wheels. The core + The ring holds the letters and the notches that turn other wheels. The core holds the wiring that does the transformation. The ring position is how many steps the core is turned relative to the ring. @@ -301,125 +236,12 @@ class Wheel(SimpleWheel): The position_l is the position of the ring, or what would be observed by the user of the Enigma machine. - The peg_positions are the number of advances of this wheel before it will + The notch_positions are the number of advances of this wheel before it will advance the next wheel. - >>> wheel_3 = Wheel(wheel_iii_spec, wheel_iii_pegs, position='b', ring_setting=1) - >>> wheel_3.position - 1 - >>> wheel_3.peg_positions - [20] - >>> wheel_3.position_l - 'b' - >>> wheel_3.advance() - >>> wheel_3.position - 2 - >>> wheel_3.peg_positions - [19] - >>> wheel_3.position_l - 'c' - - >>> wheel_6 = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', ring_setting=3) - >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase) - 'xkqhwpvngzrcfoiaselbtymjdu' - >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase) - 'ptlyrmidoxbswhnfckquzgeavj' - >>> wheel_6.position - 25 - >>> 11 in wheel_6.peg_positions - True - >>> 24 in wheel_6.peg_positions - True - >>> wheel_6.position_l - 'b' - - >>> wheel_6.advance() - >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase) - 'jpgvoumfyqbenhzrdkasxlictw' - >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase) - 'skxqlhcnwarvgmebjptyfdzuio' - >>> wheel_6.position - 0 - >>> 10 in wheel_6.peg_positions - True - >>> 23 in wheel_6.peg_positions - True - >>> wheel_6.position_l - 'c' - - >>> for _ in range(22): wheel_6.advance() - >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase) - 'mgxantkzsyqjcufirldvhoewbp' - >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase) - 'dymswobuplgraevzkqifntxcjh' - >>> wheel_6.position - 22 - >>> 1 in wheel_6.peg_positions - True - >>> 14 in wheel_6.peg_positions - True - >>> wheel_6.position_l - 'y' - - >>> wheel_6.advance() - >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase) - 'fwzmsjyrxpibtehqkcugndvaol' - >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase) - 'xlrvnatokfqzduyjphemswbigc' - >>> wheel_6.position - 23 - >>> 0 in wheel_6.peg_positions - True - >>> 13 in wheel_6.peg_positions - True - >>> wheel_6.position_l - 'z' - - >>> wheel_6.advance() - >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase) - 'vylrixqwohasdgpjbtfmcuznke' - >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase) - 'kqumzsnjepyctxiogdlrvahfbw' - >>> wheel_6.position - 24 - >>> 25 in wheel_6.peg_positions - True - >>> 12 in wheel_6.peg_positions - True - >>> wheel_6.position_l - 'a' - - >>> wheel_6.advance() - >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase) - 'xkqhwpvngzrcfoiaselbtymjdu' - >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase) - 'ptlyrmidoxbswhnfckquzgeavj' - >>> wheel_6.position - 25 - >>> 24 in wheel_6.peg_positions - True - >>> 11 in wheel_6.peg_positions - True - >>> wheel_6.position_l - 'b' - - >>> wheel_6.advance() - >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase) - 'jpgvoumfyqbenhzrdkasxlictw' - >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase) - 'skxqlhcnwarvgmebjptyfdzuio' - >>> wheel_6.position - 0 - >>> 23 in wheel_6.peg_positions - True - >>> 10 in wheel_6.peg_positions - True - >>> wheel_6.position_l - 'c' - """ - def __init__(self, transform, ring_peg_letters, ring_setting=1, position='a', raw_transform=False): - self.ring_peg_letters = ring_peg_letters + def __init__(self, transform, ring_notch_letters, ring_setting=1, position='a', raw_transform=False): + self.ring_notch_letters = ring_notch_letters self.ring_setting = ring_setting super(Wheel, self).__init__(transform, position=position, raw_transform=raw_transform) self.set_position(position) @@ -431,511 +253,35 @@ class Wheel(SimpleWheel): return object.__getattribute__(self, name) def set_position(self, position): - self.position = (pos(position) - self.ring_setting + 1) % 26 - self.peg_positions = [(pos(p) - pos(position)) % 26 for p in self.ring_peg_letters] + if isinstance(position, str): + self.position = (pos(position) - self.ring_setting + 1) % 26 + else: + self.position = (position - self.ring_setting) % 26 + # # self.notch_positions = [(pos(p) - pos(position)) % 26 for p in self.ring_notch_letters] + # self.notch_positions = [(pos(p) - (self.position + self.ring_setting - 1)) % 26 for p in self.ring_notch_letters] + self.notch_positions = [(self.position + self.ring_setting - 1 - pos(p)) % 26 for p in self.ring_notch_letters] def advance(self): super(Wheel, self).advance() - self.peg_positions = [(p - 1) % 26 for p in self.peg_positions] + self.notch_positions = [(p + 1) % 26 for p in self.notch_positions] + return self.position class Enigma(object): """An Enigma machine. - >>> enigma = Enigma(reflector_b_spec, \ - wheel_i_spec, wheel_i_pegs, \ - wheel_ii_spec, wheel_ii_pegs, \ - wheel_iii_spec, wheel_iii_pegs, \ - 1, 1, 1, \ - '') - >>> enigma.set_wheels('a', 'a', 't') - >>> enigma.wheel_positions - (0, 0, 19) - >>> cat(enigma.wheel_positions_l) - 'aat' - >>> enigma.peg_positions - ([16], [4], [2]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'puvioztjdhxmlyeawsrgbcqknf' - - >>> enigma.advance() - >>> enigma.wheel_positions - (0, 0, 20) - >>> cat(enigma.wheel_positions_l) - 'aau' - >>> enigma.peg_positions - ([16], [4], [1]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'baigpldqcowfyzjehvtsxrkumn' - - >>> enigma.advance() - >>> enigma.wheel_positions - (0, 0, 21) - >>> cat(enigma.wheel_positions_l) - 'aav' - >>> enigma.peg_positions - ([16], [4], [0]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'mnvfydiwgzsoablrxpkutchqej' - - >>> enigma.advance() - >>> enigma.wheel_positions - (0, 1, 22) - >>> cat(enigma.wheel_positions_l) - 'abw' - >>> enigma.peg_positions - ([16], [3], [25]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'ulfopcykswhbzvderqixanjtgm' - - >>> enigma.advance() - >>> enigma.wheel_positions - (0, 1, 23) - >>> cat(enigma.wheel_positions_l) - 'abx' - >>> enigma.peg_positions - ([16], [3], [24]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'qmwftdyovursbzhxaklejicpgn' - - >>> enigma.advance() - >>> enigma.wheel_positions - (0, 1, 24) - >>> cat(enigma.wheel_positions_l) - 'aby' - >>> enigma.peg_positions - ([16], [3], [23]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'oljmzxrvucybdqasngpwihtfke' - - - - - >>> enigma.set_wheels('a', 'd', 't') - >>> enigma.wheel_positions - (0, 3, 19) - >>> cat(enigma.wheel_positions_l) - 'adt' - >>> enigma.peg_positions - ([16], [1], [2]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'zcbpqxwsjiuonmldethrkygfva' - - >>> enigma.advance() - >>> enigma.wheel_positions - (0, 3, 20) - >>> cat(enigma.wheel_positions_l) - 'adu' - >>> enigma.peg_positions - ([16], [1], [1]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'ehprawjbngotxikcsdqlzyfmvu' - - >>> enigma.advance() - >>> enigma.wheel_positions - (0, 3, 21) - >>> cat(enigma.wheel_positions_l) - 'adv' - >>> enigma.peg_positions - ([16], [1], [0]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'eqzxarpihmnvjkwgbfuyslodtc' - - >>> enigma.advance() - >>> enigma.wheel_positions - (0, 4, 22) - >>> cat(enigma.wheel_positions_l) - 'aew' - >>> enigma.peg_positions - ([16], [0], [25]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'qedcbtpluzmhkongavwfirsyxj' - - >>> enigma.advance() - >>> enigma.wheel_positions - (1, 5, 23) - >>> cat(enigma.wheel_positions_l) - 'bfx' - >>> enigma.peg_positions - ([15], [25], [24]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'iwuedhsfazqxytvrkpgncoblmj' - - >>> enigma.advance() - >>> enigma.wheel_positions - (1, 5, 24) - >>> cat(enigma.wheel_positions_l) - 'bfy' - >>> enigma.peg_positions - ([15], [25], [23]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'baknstqzrmcxjdvygiefwoulph' - - - >>> enigma.set_wheels('a', 'a', 'a') - >>> ct = enigma.encipher('testmessage') - >>> ct - 'olpfhnvflyn' - - >>> enigma.set_wheels('a', 'd', 't') - >>> ct = enigma.encipher('testmessage') - >>> ct - 'lawnjgpwjik' - - - >>> enigma.set_wheels('b', 'd', 'q') - >>> ct = enigma.encipher('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') - >>> ct - 'kvmmwrlqlqsqpeugjrcxzwpfyiyybwloewrouvkpoztceuwtfjzqwpbqldttsr' - >>> enigma.left_wheel.position_l - 'c' - >>> enigma.middle_wheel.position_l - 'h' - >>> enigma.right_wheel.position_l - 'a' - - # Setting sheet line 31 from http://www.codesandciphers.org.uk/enigma/enigma3.htm - # Enigma simulation settings are - # http://enigma.louisedade.co.uk/enigma.html?m3;b;b153;AFTX;AJEU;AU-BG-EY-FP-HL-IN-JZ-OS-QR-TX - >>> enigma31 = Enigma(reflector_b_spec, \ - wheel_i_spec, wheel_i_pegs, \ - wheel_v_spec, wheel_v_pegs, \ - wheel_iii_spec, wheel_iii_pegs, \ - 6, 20, 24, \ - 'ua pf rq so ni ey bg hl tx zj') - - >>> enigma31.set_wheels('j', 'e', 'u') - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (4, 11, 24) - >>> cat(enigma31.wheel_positions_l) - 'jev' - >>> enigma31.peg_positions - ([7], [21], [0]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'mvqjlyowkdieasgzcunxrbhtfp' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (4, 12, 25) - >>> cat(enigma31.wheel_positions_l) - 'jfw' - >>> enigma31.peg_positions - ([7], [20], [25]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'sjolzuyvrbwdpxcmtiaqfhknge' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (4, 12, 0) - >>> cat(enigma31.wheel_positions_l) - 'jfx' - >>> enigma31.peg_positions - ([7], [20], [24]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'qrxedkoywufmlvgsabpzjnicht' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (4, 12, 1) - >>> cat(enigma31.wheel_positions_l) - 'jfy' - >>> enigma31.peg_positions - ([7], [20], [23]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'hpsukliagqefwvtbjxcodnmrzy' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (4, 12, 2) - >>> cat(enigma31.wheel_positions_l) - 'jfz' - >>> enigma31.peg_positions - ([7], [20], [22]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'zevnbpyqowrtxdifhkulscjmga' - - - >>> enigma31.set_wheels('i', 'd', 'z') - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 3) - >>> cat(enigma31.wheel_positions_l) - 'ida' - >>> enigma31.peg_positions - ([8], [22], [21]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'ikhpqrvcambzjondefwyxgsutl' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 4) - >>> cat(enigma31.wheel_positions_l) - 'idb' - >>> enigma31.peg_positions - ([8], [22], [20]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'cdabskhgzwfmlqvunyexpojtri' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 5) - >>> cat(enigma31.wheel_positions_l) - 'idc' - >>> enigma31.peg_positions - ([8], [22], [19]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'pcbwiqhgemyvjsuaftnroldzkx' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 6) - >>> cat(enigma31.wheel_positions_l) - 'idd' - >>> enigma31.peg_positions - ([8], [22], [18]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'xcbfvdnouptmlghjzwykierasq' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 7) - >>> cat(enigma31.wheel_positions_l) - 'ide' - >>> enigma31.peg_positions - ([8], [22], [17]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'xfvglbdynuseriwqpmkzjcoaht' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 8) - >>> cat(enigma31.wheel_positions_l) - 'idf' - >>> enigma31.peg_positions - ([8], [22], [16]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'tfpqlbouynsewjgcdxkahzmriv' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 9) - >>> cat(enigma31.wheel_positions_l) - 'idg' - >>> enigma31.peg_positions - ([8], [22], [15]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'cjaunvlwtbygzexrspqidfhokm' - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 10) - >>> cat(enigma31.wheel_positions_l) - 'idh' - >>> enigma31.peg_positions - ([8], [22], [14]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'yltxkrqvowebzpingfucshjdam' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 11) - >>> cat(enigma31.wheel_positions_l) - 'idi' - >>> enigma31.peg_positions - ([8], [22], [13]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'myktluzrnxceaiqsohpdfwvjbg' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 12) - >>> cat(enigma31.wheel_positions_l) - 'idj' - >>> enigma31.peg_positions - ([8], [22], [12]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'pynjrmiugdqxfcvakewzhoslbt' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 13) - >>> cat(enigma31.wheel_positions_l) - 'idk' - >>> enigma31.peg_positions - ([8], [22], [11]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'mwvedyplnoxhaijgrqtszcbkfu' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 14) - >>> cat(enigma31.wheel_positions_l) - 'idl' - >>> enigma31.peg_positions - ([8], [22], [10]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'qcbrfeutvoxpnmjladzhgiykws' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 15) - >>> cat(enigma31.wheel_positions_l) - 'idm' - >>> enigma31.peg_positions - ([8], [22], [9]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'dnoahryetsmukbcvwfjilpqzgx' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 16) - >>> cat(enigma31.wheel_positions_l) - 'idn' - >>> enigma31.peg_positions - ([8], [22], [8]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'nidcfehgbqsovalyjzkxwmutpr' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 17) - >>> cat(enigma31.wheel_positions_l) - 'ido' - >>> enigma31.peg_positions - ([8], [22], [7]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'joifxdulcarhzpbntkwqgysevm' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 18) - >>> cat(enigma31.wheel_positions_l) - 'idp' - >>> enigma31.peg_positions - ([8], [22], [6]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'ptnlsxvozmwdjchayuebrgkfqi' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 19) - >>> cat(enigma31.wheel_positions_l) - 'idq' - >>> enigma31.peg_positions - ([8], [22], [5]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'slwopzqnmxybihdeguavrtcjkf' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 20) - >>> cat(enigma31.wheel_positions_l) - 'idr' - >>> enigma31.peg_positions - ([8], [22], [4]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'hcbedwlamzogixkytsrqvufnpj' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 21) - >>> cat(enigma31.wheel_positions_l) - 'ids' - >>> enigma31.peg_positions - ([8], [22], [3]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'odxbjwzrmelkisavuhnyqpfctg' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 22) - >>> cat(enigma31.wheel_positions_l) - 'idt' - >>> enigma31.peg_positions - ([8], [22], [2]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'udgbfeclrwnhxksvtioqapjmzy' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 23) - >>> cat(enigma31.wheel_positions_l) - 'idu' - >>> enigma31.peg_positions - ([8], [22], [1]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'nrdczqxmowvshaiufblypkjgte' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 24) - >>> cat(enigma31.wheel_positions_l) - 'idv' - >>> enigma31.peg_positions - ([8], [22], [0]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'hkifjdoacebqtzgulyvmpsxwrn' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 11, 25) - >>> cat(enigma31.wheel_positions_l) - 'iew' - >>> enigma31.peg_positions - ([8], [21], [25]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'yptzuhofqvnmlkgbixwcejsrad' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 11, 0) - >>> cat(enigma31.wheel_positions_l) - 'iex' - >>> enigma31.peg_positions - ([8], [21], [24]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'vkdcwhqfjibzsptngumoraeyxl' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 11, 1) - >>> cat(enigma31.wheel_positions_l) - 'iey' - >>> enigma31.peg_positions - ([8], [21], [23]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'wenpbqrouxlkychdfgzvitajms' - - - >>> enigma31.set_wheels('i', 'z', 'd') - >>> enigma31.encipher('verylongtestmessagewithanextrabitofmessageforgoodmeasure') - 'apocwtjuikurcfivlozvhffkoacxufcekthcvodfqpxdjqyckdozlqki' - >>> enigma31.wheel_positions - (4, 9, 10) - >>> cat(enigma31.wheel_positions_l) - 'jch' - >>> enigma31.peg_positions - ([7], [23], [14]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'mopnigfuesqwadbcktjrhylzvx' - - >>> enigma31.set_wheels('i', 'z', 'd') - >>> enigma31.decipher('apocwtjuikurcfivlozvhffkoacxufcekthcvodfqpxdjqyckdozlqki') - 'verylongtestmessagewithanextrabitofmessageforgoodmeasure' """ def __init__(self, reflector_spec, - left_wheel_spec, left_wheel_pegs, - middle_wheel_spec, middle_wheel_pegs, - right_wheel_spec, right_wheel_pegs, + left_wheel_spec, left_wheel_notches, + middle_wheel_spec, middle_wheel_notches, + right_wheel_spec, right_wheel_notches, left_ring_setting, middle_ring_setting, right_ring_setting, plugboard_setting): self.reflector = Reflector(reflector_spec) - self.left_wheel = Wheel(left_wheel_spec, left_wheel_pegs, ring_setting=left_ring_setting) - self.middle_wheel = Wheel(middle_wheel_spec, middle_wheel_pegs, ring_setting=middle_ring_setting) - self.right_wheel = Wheel(right_wheel_spec, right_wheel_pegs, ring_setting=right_ring_setting) + self.left_wheel = Wheel(left_wheel_spec, left_wheel_notches, ring_setting=left_ring_setting) + self.middle_wheel = Wheel(middle_wheel_spec, middle_wheel_notches, ring_setting=middle_ring_setting) + self.right_wheel = Wheel(right_wheel_spec, right_wheel_notches, ring_setting=right_ring_setting) self.plugboard = Plugboard(plugboard_setting) def __getattribute__(self,name): @@ -943,8 +289,8 @@ class Enigma(object): return self.left_wheel.position, self.middle_wheel.position, self.right_wheel.position elif name=='wheel_positions_l': return self.left_wheel.position_l, self.middle_wheel.position_l, self.right_wheel.position_l - elif name=='peg_positions': - return self.left_wheel.peg_positions, self.middle_wheel.peg_positions, self.right_wheel.peg_positions + elif name=='notch_positions': + return self.left_wheel.notch_positions, self.middle_wheel.notch_positions, self.right_wheel.notch_positions else: return object.__getattribute__(self, name) @@ -968,9 +314,9 @@ class Enigma(object): def advance(self): advance_middle = False advance_left = False - if 0 in self.right_wheel.peg_positions: + if 0 in self.right_wheel.notch_positions: advance_middle = True - if 0 in self.middle_wheel.peg_positions: + if 0 in self.middle_wheel.notch_positions: advance_left = True advance_middle = True self.right_wheel.advance() @@ -995,191 +341,11 @@ class Enigma(object): # print('enigma.advance()') # print("assert(enigma.wheel_positions == {})".format(enigma.wheel_positions)) # print("assert(cat(enigma.wheel_positions_l) == '{}')".format(cat(enigma.wheel_positions_l))) -# print("assert(enigma.peg_positions == {})".format(enigma.peg_positions)) +# print("assert(enigma.notch_positions == {})".format(enigma.notch_positions)) # print("assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == '{}')".format(cat(enigma.lookup(l) for l in string.ascii_lowercase))) # print() -################################## -# # Bombe -################################## - -Signal = collections.namedtuple('Signal', ['bank', 'wire']) -Connection = collections.namedtuple('Connection', ['banks', 'scrambler']) -MenuItem = collections.namedtuple('MenuIem', ['before', 'after', 'number']) - - -class Scrambler(object): - def __init__(self, wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec, - wheel1_pos='a', wheel2_pos='a', wheel3_pos='a'): - self.wheel1 = SimpleWheel(wheel1_spec, position=wheel1_pos) - self.wheel2 = SimpleWheel(wheel2_spec, position=wheel2_pos) - self.wheel3 = SimpleWheel(wheel3_spec, position=wheel3_pos) - self.reflector = Reflector(reflector_spec) - - def __getattribute__(self, name): - if name=='wheel_positions': - return self.wheel1.position, self.wheel2.position, self.wheel3.position - elif name=='wheel_positions_l': - return self.wheel1.position_l, self.wheel2.position_l, self.wheel3.position_l - else: - return object.__getattribute__(self, name) - - def advance(self, wheel1=False, wheel2=False, wheel3=True): - if wheel1: self.wheel1.advance() - if wheel2: self.wheel2.advance() - if wheel3: self.wheel3.advance() - - def lookup(self, letter): - a = self.wheel3.forward(letter) - b = self.wheel2.forward(a) - c = self.wheel1.forward(b) - d = self.reflector.forward(c) - e = self.wheel1.backward(d) - f = self.wheel2.backward(e) - g = self.wheel3.backward(f) - return g - - def set_positions(self, wheel1_pos, wheel2_pos, wheel3_pos): - self.wheel1.set_position(wheel1_pos) - self.wheel2.set_position(wheel2_pos) - self.wheel3.set_position(wheel3_pos) - - -class Bombe(object): - def __init__(self, wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec, - menu=None, start_signal=None, use_diagonal_board=True, - verify_plugboard=True): - self.connections = [] - self.wheel1_spec = wheel1_spec - self.wheel2_spec = wheel2_spec - self.wheel3_spec = wheel3_spec - self.reflector_spec = reflector_spec - if menu: - self.read_menu(menu) - if start_signal: - self.test_start = start_signal - self.use_diagonal_board = use_diagonal_board - self.verify_plugboard = verify_plugboard - - def __getattribute__(self, name): - if name=='wheel_positions': - return self.connections[0].scrambler.wheel_positions - elif name=='wheel_positions_l': - return self.connections[0].scrambler.wheel_positions_l - else: - return object.__getattribute__(self, name) - - def __call__(self, start_positions): - return start_positions, self.test(initial_signal=self.test_start, - start_positions=start_positions, - use_diagonal_board=self.use_diagonal_board, - verify_plugboard=self.verify_plugboard) - - def add_connection(self, bank_before, bank_after, scrambler): - self.connections += [Connection([bank_before, bank_after], scrambler)] - - def read_menu(self, menu): - for item in menu: - scrambler = Scrambler(self.wheel1_spec, self.wheel2_spec, self.wheel3_spec, - self.reflector_spec, - wheel3_pos=unpos(item.number - 1)) - self.add_connection(item.before, item.after, scrambler) - most_common_letter = (collections.Counter(m.before for m in menu) + \ - collections.Counter(m.after for m in menu)).most_common(1)[0][0] - self.test_start = Signal(most_common_letter, most_common_letter) - - def set_positions(self, wheel1_pos, wheel2_pos, wheel3_pos): - for i, c in enumerate(self.connections): - c.scrambler.set_positions(wheel1_pos, wheel2_pos, unpos(pos(wheel3_pos) + i)) - - def test(self, initial_signal=None, start_positions=None, use_diagonal_board=True, - verify_plugboard=True): - self.banks = {label: - dict(zip(string.ascii_lowercase, [False]*len(string.ascii_lowercase))) - for label in string.ascii_lowercase} - if start_positions: - self.set_positions(*start_positions) - if not initial_signal: - initial_signal = self.test_start - self.pending = [initial_signal] - self.propagate(use_diagonal_board) - live_wire_count = len([self.banks[self.test_start.bank][w] - for w in self.banks[self.test_start.bank] - if self.banks[self.test_start.bank][w]]) - if live_wire_count < 26: - if verify_plugboard: - possibles = self.possible_plugboards() - return all(s0.isdisjoint(s1) for s0 in possibles for s1 in possibles if s0 != s1) - else: - return True - else: - return False - - def propagate(self, use_diagonal_board): - while self.pending: - current = self.pending[0] - # print("processing", current) - self.pending = self.pending[1:] - if not self.banks[current.bank][current.wire]: - self.banks[current.bank][current.wire] = True - if use_diagonal_board: - self.pending += [Signal(current.wire, current.bank)] - for c in self.connections: - if current.bank in c.banks: - other_bank = [b for b in c.banks if b != current.bank][0] - other_wire = c.scrambler.lookup(current.wire) - # print(" adding", other_bank, other_wire, "because", c.banks) - self.pending += [Signal(other_bank, other_wire)] - - def run(self, run_start=None, wheel1_pos='a', wheel2_pos='a', wheel3_pos='a', use_diagonal_board=True): - if not run_start: - run_start = self.test_start - self.solutions = [] - self.set_positions(wheel1_pos, wheel2_pos, wheel3_pos) - for run_index in range(26*26*26): - if self.test(initial_signal=run_start, use_diagonal_board=use_diagonal_board): - self.solutions += [self.connections[0].scrambler.wheel_positions_l] - advance3 = True - advance2 = False - advance1 = False - if (run_index + 1) % 26 == 0: advance2 = True - if (run_index + 1) % (26*26) == 0: advance1 = True - for c in self.connections: - c.scrambler.advance(advance1, advance2, advance3) - return self.solutions - - def possible_plugboards(self): - possibles = set() - for b in self.banks: - active = [w for w in self.banks[b] if self.banks[b][w]] - inactive = [w for w in self.banks[b] if not self.banks[b][w]] - if len(active) == 1: - possibles = possibles.union({frozenset((b, active[0]))}) - if len(inactive) == 1: - possibles = possibles.union({frozenset((b, inactive[0]))}) - return possibles - - -def make_menu(plaintext, ciphertext): - return [MenuItem(p, c, i+1) - for i, (p, c) in enumerate(zip(plaintext, ciphertext))] - - -def run_multi_bombe(wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec, menu, - start_signal=None, use_diagonal_board=True, - verify_plugboard=True): - allwheels = itertools.product(string.ascii_lowercase, repeat=3) - - with multiprocessing.Pool() as pool: - res = pool.map(Bombe(wheel1_spec, wheel2_spec, wheel3_spec, - reflector_spec, menu=menu, start_signal=start_signal, - use_diagonal_board=use_diagonal_board, - verify_plugboard=verify_plugboard), - allwheels) - return [r[0] for r in res if r[1]] - - if __name__ == "__main__": import doctest # doctest.testmod(extraglobs={'lt': LetterTransformer(1, 'a')})