# HG changeset patch # User Brian Neal # Date 1386039516 21600 # Node ID 672ba8b2269fb5ca4fab3ab95387b4dc645fbe65 # Parent eea5734a16552165140319142f43a33b46d99c47 Added encrypt() and test. diff -r eea5734a1655 -r 672ba8b2269f purple/machine.py --- a/purple/machine.py Mon Dec 02 20:44:26 2013 -0600 +++ b/purple/machine.py Mon Dec 02 20:58:36 2013 -0600 @@ -206,6 +206,38 @@ return ''.join(plaintext) + def encrypt(self, plaintext): + """Encrypts the given plaintext message and returns the ciphertext + output. + + plaintext must contain only the letters A-Z or else a Purple97Error + exception is raised. + + """ + ciphertext = [] + for c in plaintext: + if c not in self.VALID_KEYS: + raise Purple97Error("invalid input '{}' to encrypt".format(c)) + + n = self.plugboard[c] + if n < 6: + # This input goes to the sixes switch + x = self.sixes.encrypt(n) + else: + # This input goes to the chain of twenties switches in reverse + # order compared to decrypt. + n -= 6 + x = self.twenties[2].encrypt(self.twenties[1].encrypt( + self.twenties[0].encrypt(n))) + x += 6 + + ciphertext.append(self.alphabet[x]) + + # Now step the switches. + self.step() + + return ''.join(ciphertext) + def step(self): """Step the stepping switches.""" # First read the sixes and middle switch diff -r eea5734a1655 -r 672ba8b2269f purple/tests/test_machine.py --- a/purple/tests/test_machine.py Mon Dec 02 20:44:26 2013 -0600 +++ b/purple/tests/test_machine.py Mon Dec 02 20:58:36 2013 -0600 @@ -9,6 +9,25 @@ from purple.switch import SteppingSwitchError +# The ciphertext contains garbles, indicated by '-' chars. +ciphertext = ( + "ZTXODNWKCCMAVNZXYWEETUQTCIMN" + "VEUVIWBLUAXRRTLVARGNTPCNOIUP" + "JLCIVRTPJKAUHVMUDTHKTXYZELQTVWGBUHFAWSHU" + "LBFBHEXMYHFLOWD-KWHKKNXEBVPYHHGHEKXIOHQ" + "HUHWIKYJYHPPFEALNNAKIBOOZNFRLQCFLJTTSSDDOIOCVT-" + "ZCKQTSHXTIJCNWXOKUFNQR-TAOIHWTATWV" +) + +plaintext = ( + "FOVTATAKIDASINIMUIMINOMOXIWO" + "IRUBESIFYXXFCKZZRDXOOVBTNFYX" + "FAEMEMORANDUMFIOFOVOOMOJIBAKARIFYXRAICCY" + "LFCBBCFCTHEGOVE-NMENTOFJAPANLFLPROMPTED" + "BYAGENUINEDESIRETOCOMETOANAMICABLEUNDERSTANDIN-" + "WITHTHEGOVERNMENTOFTHE-NITEDSTATES" +) + class Purple97TestCase(unittest.TestCase): def test_construction(self): @@ -92,31 +111,11 @@ self.assertRaises(Purple97Error, Purple97.from_key_sheet, '1-9,2,20-1a') self.assertRaises(Purple97Error, Purple97.from_key_sheet, '1-9,2,20-123') - def test_first_part_of_14_part_message(self): - - # The ciphertext contains garbles, indicated by '-' chars. - ciphertext = ( - "ZTXODNWKCCMAVNZXYWEETUQTCIMN" - "VEUVIWBLUAXRRTLVARGNTPCNOIUP" - "JLCIVRTPJKAUHVMUDTHKTXYZELQTVWGBUHFAWSHU" - "LBFBHEXMYHFLOWD-KWHKKNXEBVPYHHGHEKXIOHQ" - "HUHWIKYJYHPPFEALNNAKIBOOZNFRLQCFLJTTSSDDOIOCVT-" - "ZCKQTSHXTIJCNWXOKUFNQR-TAOIHWTATWV" - ) - - self.assertEqual(216, len(ciphertext)) + def test_decrypt_first_part_of_14_part_message(self): # Use 'X' in place of the garbles input_text = ciphertext.replace('-', 'X') - plaintext = ( - "FOVTATAKIDASINIMUIMINOMOXIWO" - "IRUBESIFYXXFCKZZRDXOOVBTNFYX" - "FAEMEMORANDUMFIOFOVOOMOJIBAKARIFYXRAICCY" - "LFCBBCFCTHEGOVE-NMENTOFJAPANLFLPROMPTED" - "BYAGENUINEDESIRETOCOMETOANAMICABLEUNDERSTANDIN-" - "WITHTHEGOVERNMENTOFTHE-NITEDSTATES" - ) self.assertEqual(len(ciphertext), len(plaintext)) # Decrypt @@ -137,3 +136,29 @@ mismatches) self.assertTrue(len(mismatches) == 0, msg) + + def test_encrypt_first_part_of_14_part_message(self): + + # Use 'X' in place of the garbles + input_text = plaintext.replace('-', 'X') + + self.assertEqual(len(ciphertext), len(plaintext)) + + # Encrypt + purple = Purple97.from_key_sheet( + switches='9-1,24,6-23', + alphabet='NOKTYUXEQLHBRMPDICJASVWGZF') + + actual = purple.encrypt(input_text) + + mismatches = [] + for n, (a, b) in enumerate(zip(ciphertext, actual)): + if a != b and a != '-': + mismatches.append(n) + + msg = None + if mismatches: + msg = "There are {} mismatches: {}".format(len(mismatches), + mismatches) + + self.assertTrue(len(mismatches) == 0, msg)