# HG changeset patch # User Brian Neal # Date 1385951406 21600 # Node ID 2e3d919383ddb7b8d299259f133cd86cefeabdfb # Parent 7d32247eab5e402871350d3472594995e59bedb5 Fixed bug in stepping logic. 216 letter test now passes. diff -r 7d32247eab5e -r 2e3d919383dd purple/machine.py --- a/purple/machine.py Sun Dec 01 16:56:45 2013 -0600 +++ b/purple/machine.py Sun Dec 01 20:30:06 2013 -0600 @@ -10,6 +10,9 @@ Sullivan, and Frode Weierud. This paper was published in Cryptologia, Volume 27, Issue 1, January, 2003, pp. 1-43. +The paper is also available at: +http://cryptocellar.web.cern.ch/cryptocellar/pubs/PurpleRevealed.pdf + """ from collections import Counter import string @@ -197,11 +200,16 @@ plaintext.append(self.alphabet[x]) - # Now step the switches. - # The sixes switch steps every letter. - sixes_pos = self.sixes.step() + # Now step the switches. First read the sixes and middle switch + # positions before stepping anything. Use these latched values in + # the decision processes for stepping a twenties. This is crucial! + sixes_pos = self.sixes.pos middle_pos = self.middle_switch.pos + # Now we can step the sixes. It unconditionally steps after every + # letter is processed. + self.sixes.step() + # Only 1 twenties switch steps at a time. # # Normally the fast switch advances every letter. @@ -230,7 +238,7 @@ # Here we see the fast switch stepping. However at (*) both the # sixes and middle switch have reached their last positions, so the # slow switch steps. At the next letter (#) the middle switch plays - # "catch up" and advances. + # "catch up" and advances while the fast switch holds steady. if sixes_pos == 24 and middle_pos == 24: self.slow_switch.step() diff -r 7d32247eab5e -r 2e3d919383dd purple/tests/test_machine.py --- a/purple/tests/test_machine.py Sun Dec 01 16:56:45 2013 -0600 +++ b/purple/tests/test_machine.py Sun Dec 01 20:30:06 2013 -0600 @@ -104,17 +104,10 @@ "ZCKQTSHXTIJCNWXOKUFNQR-TAOIHWTATWV" ) - ciphertext = """\ -ZTXOD NWKCC MAVNZ XYWEE TUQTC IMNVE UVIWB LUAXR RTLVA -RGNTP CNOIU PJLCI VRTPJ KAUHV MUDTH KTXYZ ELQTV WGBUH FAWSH -ULBFB HEXMY HFLOW D-KWH KKNXE BVPYH HGHEK XIOHQ HUHWI KYJYH -PPFEA LNNAK IBOOZ NFRLQ CFLJT TSSDD OIOCV T-ZCK QTSHX TIJCN -WXOKU FNQR- TAOIH WTATW V""" - ciphertext = ''.join(ciphertext.split()) self.assertEqual(216, len(ciphertext)) # Use 'X' in place of the garbles - input_text = ciphertext.replace('-', 'A') + input_text = ciphertext.replace('-', 'X') plaintext = ( "FOVTATAKIDASINIMUIMINOMOXIWO" @@ -124,6 +117,7 @@ "BYAGENUINEDESIRETOCOMETOANAMICABLEUNDERSTANDIN-" "WITHTHEGOVERNMENTOFTHE-NITEDSTATES" ) + self.assertEqual(len(ciphertext), len(plaintext)) # Decrypt purple = Purple97.from_key_sheet( @@ -131,15 +125,15 @@ alphabet='NOKTYUXEQLHBRMPDICJASVWGZF') actual = purple.decrypt(input_text) - print(actual) mismatches = [] for n, (a, b) in enumerate(zip(plaintext, actual)): if a != b and a != '-': mismatches.append(n) - print("There are {} mismatches".format(len(mismatches))) + msg = None if mismatches: - print("mismatches = {}".format(mismatches)) + msg = "There are {} mismatches: {}".format(len(mismatches), + mismatches) - self.assertTrue(len(mismatches) == 0) + self.assertTrue(len(mismatches) == 0, msg)