Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Changes from unit testing.

  • Loading branch information...
commit 24fc561df014dc7eb8f7c6f4349c8722227cd7d3 1 parent dd74989
Al Sweigart authored
2  affineBreaker.py
@@ -7,7 +7,7 @@
7 7
8 8 def main():
9 9 # You might want to copy & paste this text from the source code at
10   - # http://inventwithpython.com/affineBreaker.py
  10 + # http://invpy.com/affineBreaker.py
11 11 myMessage = 'H RZPEDYBO NZDKW WBTBOIB YZ MB RHKKBW VUYBKKVLBUY VG VY RZDKW WBRBVIB H QDPHU VUYZ MBKVBIVUL YQHY VY NHT QDPHU. -HKHU YDOVUL'
12 12
13 13 brokenCiphertext = breakAffine(myMessage.upper())
4 caesarBreaker.py
@@ -13,7 +13,7 @@
13 13
14 14 # The rest of the program is the same as the original Caesar program:
15 15
16   - # run the encryption/decryption code on each symbol in the message string
  16 + # run the encryption/decryption code on each symbol in the message
17 17 for symbol in message:
18 18 if symbol in LETTERS:
19 19 num = LETTERS.find(symbol) # get the number of the symbol
@@ -23,7 +23,7 @@
23 23 if num < 0:
24 24 num = num + len(LETTERS)
25 25
26   - # add encrypted/decrypted number's symbol at the end of translated
  26 + # add number's symbol at the end of translated
27 27 translated = translated + LETTERS[num]
28 28
29 29 else:
6 caesarCipher.py
@@ -13,7 +13,7 @@
13 13 mode = 'encrypt' # set to 'encrypt' or 'decrypt'
14 14
15 15 # every possible symbol that can be encrypted
16   -LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  16 +LETTERS = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'
17 17
18 18 # stores the encrypted/decrypted form of the message
19 19 translated = ''
@@ -31,8 +31,8 @@
31 31 elif mode == 'decrypt':
32 32 num = num - key
33 33
34   - # handle the wrap around if num is larger than the length of LETTERS
35   - # or less than 0
  34 + # handle the wrap around if num is larger than the length of
  35 + # LETTERS or less than 0
36 36 if num >= len(LETTERS):
37 37 num = num - len(LETTERS)
38 38 elif num < 0:
29 codebreaker_unit_tests.py
@@ -296,13 +296,10 @@ def test_transpositionTestProgram(self):
296 296 self.assertTrue('Transposition cipher test passed.' in procOut)
297 297
298 298
299   - def test_detectEnglishProgram(self):
300   - proc = subprocess.Popen('c:\\python32\\python.exe detectEnglish.py', stdout=subprocess.PIPE, stdin=subprocess.PIPE)
301   - procOut = proc.communicate()[0].decode('ascii')
302   -
303   - expectedOutput = 'Testing the English detection module...\nThe quick brown fox jumped over the yellow lazy dog.\n\tTrue\n\nHello there. lkjjfldsf dsafk alf ewfewlfjl efa\n\tTrue\n\nSumimasen. Kore wa nan desu ka?\n\tFalse\n\n1100010110010111001011110000\n\tFalse\n\n'
  299 + def test_detectEnglishModule(self):
  300 + import detectEnglish
304 301
305   - self.assertEqual(procOut, expectedOutput)
  302 + self.assertTrue(detectEnglish.isEnglish(FOX_MESSAGE))
306 303
307 304
308 305 def test_affineCipherProgram(self):
@@ -441,28 +438,12 @@ def test_vigenereBreakerProgram(self):
441 438 procOut = proc.communicate('D\n'.encode('ascii'))[0].decode('ascii')
442 439
443 440 expectedClipboard = """ALAN MATHISON TURING WAS A BRITISH MATHEMATICIAN, LOGICIAN, CRYPTANALYST, AND COMPUTER SCIENTIST. HE WAS HIGHLY INFLUENTIAL IN THE DEVELOPMENT OF COMPUTER SCIENCE, PROVIDING A FORMALISATION OF THE CONCEPTS OF "ALGORITHM" AND "COMPUTATION" WITH THE TURING MACHINE. TURING IS WIDELY CONSIDERED TO BE THE FATHER OF COMPUTER SCIENCE AND ARTIFICIAL INTELLIGENCE. DURING WORLD WAR II, TURING WORKED FOR THE GOVERNMENT CODE AND CYPHER SCHOOL (GCCS) AT BLETCHLEY PARK, BRITAIN'S CODEBREAKING CENTRE. FOR A TIME HE WAS HEAD OF HUT 8, THE SECTION RESPONSIBLE FOR GERMAN NAVAL CRYPTANALYSIS. HE DEVISED A NUMBER OF TECHNIQUES FOR BREAKING GERMAN CIPHERS, INCLUDING THE METHOD OF THE BOMBE, AN ELECTROMECHANICAL MACHINE THAT COULD FIND SETTINGS FOR THE ENIGMA MACHINE. AFTER THE WAR HE WORKED AT THE NATIONAL PHYSICAL LABORATORY, WHERE HE CREATED ONE OF THE FIRST DESIGNS FOR A STORED-PROGRAM COMPUTER, THE ACE. IN 1948 TURING JOINED MAX NEWMAN'S COMPUTING LABORATORY AT MANCHESTER UNIVERSITY, WHERE HE ASSISTED IN THE DEVELOPMENT OF THE MANCHESTER COMPUTERS AND BECAME INTERESTED IN MATHEMATICAL BIOLOGY. HE WROTE A PAPER ON THE CHEMICAL BASIS OF MORPHOGENESIS, AND PREDICTED OSCILLATING CHEMICAL REACTIONS SUCH AS THE BELOUSOV-ZHABOTINSKY REACTION, WHICH WERE FIRST OBSERVED IN THE 1960S. TURING'S HOMOSEXUALITY RESULTED IN A CRIMINAL PROSECUTION IN 1952, WHEN HOMOSEXUAL ACTS WERE STILL ILLEGAL IN THE UNITED KINGDOM. HE ACCEPTED TREATMENT WITH FEMALE HORMONES (CHEMICAL CASTRATION) AS AN ALTERNATIVE TO PRISON. TURING DIED IN 1954, JUST OVER TWO WEEKS BEFORE HIS 42ND BIRTHDAY, FROM CYANIDE POISONING. AN INQUEST DETERMINED THAT HIS DEATH WAS SUICIDE; HIS MOTHER AND SOME OTHERS BELIEVED HIS DEATH WAS ACCIDENTAL. ON 10 SEPTEMBER 2009, FOLLOWING AN INTERNET CAMPAIGN, BRITISH PRIME MINISTER GORDON BROWN MADE AN OFFICIAL PUBLIC APOLOGY ON BEHALF OF THE BRITISH GOVERNMENT FOR "THE APPALLING WAY HE WAS TREATED." AS OF MAY 2012 A PRIVATE MEMBER'S BILL WAS BEFORE THE HOUSE OF LORDS WHICH WOULD GRANT TURING A STATUTORY PARDON IF ENACTED."""
444   - expectedOutput = 'Determining most likely key lengths with Kasiski Examination...\nKasiski Examination results say the most likely key lengths are: 3 2 6 4 12 \n\nPossible letters for letter 1 of the key: A L M \nPossible letters for letter 2 of the key: S N O \nPossible letters for letter 3 of the key: V I Z \nAttempting with key: ASV\nAttempting with key: ASI\nAttempting with key: ASZ\nAttempting with key: ANV\nAttempting with key: ANI\nAttempting with key: ANZ\nAttempting with key: AOV\nAttempting with key: AOI\nAttempting with key: AOZ\nAttempting with key: LSV\nAttempting with key: LSI\nAttempting with key: LSZ\nAttempting with key: LNV\nAttempting with key: LNI\nAttempting with key: LNZ\nAttempting with key: LOV\nAttempting with key: LOI\nAttempting with key: LOZ\nAttempting with key: MSV\nAttempting with key: MSI\nAttempting with key: MSZ\nAttempting with key: MNV\nAttempting with key: MNI\nAttempting with key: MNZ\nAttempting with key: MOV\nAttempting with key: MOI\nAttempting with key: MOZ\nPossible letters for letter 1 of the key: O A E \nPossible letters for letter 2 of the key: M S I \nAttempting with key: OM\nAttempting with key: OS\nAttempting with key: OI\nAttempting with key: AM\nAttempting with key: AS\nAttempting with key: AI\nAttempting with key: EM\nAttempting with key: ES\nAttempting with key: EI\nPossible letters for letter 1 of the key: A E O \nPossible letters for letter 2 of the key: S D G \nPossible letters for letter 3 of the key: I V X \nPossible letters for letter 4 of the key: M Z Q \nPossible letters for letter 5 of the key: O B Z \nPossible letters for letter 6 of the key: V I K \nAttempting with key: ASIMOV\n\nPossible encryption break:\nKey ASIMOV: ALAN MATHISON TURING WAS A BRITISH MATHEMATICIAN, LOGICIAN, CRYPTANALYST, AND COMPUTER SCIENTIST. HE WAS HIGHLY INFLUENTIAL IN THE DEVELOPMENT OF COMPUTER SCIENCE, PROVIDING A FORMALISATION OF THE CON\n\nEnter D for done, or just press Enter to continue breaking:\n> Copying broken ciphertext to clipboard:\nALAN MATHISON TURING WAS A BRITISH MATHEMATICIAN, LOGICIAN, CRYPTANALYST, AND COMPUTER SCIENTIST. HE WAS HIGHLY INFLUENTIAL IN THE DEVELOPMENT OF COMPUTER SCIENCE, PROVIDING A FORMALISATION OF THE CONCEPTS OF "ALGORITHM" AND "COMPUTATION" WITH THE TURING MACHINE. TURING IS WIDELY CONSIDERED TO BE THE FATHER OF COMPUTER SCIENCE AND ARTIFICIAL INTELLIGENCE. DURING WORLD WAR II, TURING WORKED FOR THE GOVERNMENT CODE AND CYPHER SCHOOL (GCCS) AT BLETCHLEY PARK, BRITAIN\'S CODEBREAKING CENTRE. FOR A TIME HE WAS HEAD OF HUT 8, THE SECTION RESPONSIBLE FOR GERMAN NAVAL CRYPTANALYSIS. HE DEVISED A NUMBER OF TECHNIQUES FOR BREAKING GERMAN CIPHERS, INCLUDING THE METHOD OF THE BOMBE, AN ELECTROMECHANICAL MACHINE THAT COULD FIND SETTINGS FOR THE ENIGMA MACHINE. AFTER THE WAR HE WORKED AT THE NATIONAL PHYSICAL LABORATORY, WHERE HE CREATED ONE OF THE FIRST DESIGNS FOR A STORED-PROGRAM COMPUTER, THE ACE. IN 1948 TURING JOINED MAX NEWMAN\'S COMPUTING LABORATORY AT MANCHESTER UNIVERSITY, WHERE HE ASSISTED IN THE DEVELOPMENT OF THE MANCHESTER COMPUTERS AND BECAME INTERESTED IN MATHEMATICAL BIOLOGY. HE WROTE A PAPER ON THE CHEMICAL BASIS OF MORPHOGENESIS, AND PREDICTED OSCILLATING CHEMICAL REACTIONS SUCH AS THE BELOUSOV-ZHABOTINSKY REACTION, WHICH WERE FIRST OBSERVED IN THE 1960S. TURING\'S HOMOSEXUALITY RESULTED IN A CRIMINAL PROSECUTION IN 1952, WHEN HOMOSEXUAL ACTS WERE STILL ILLEGAL IN THE UNITED KINGDOM. HE ACCEPTED TREATMENT WITH FEMALE HORMONES (CHEMICAL CASTRATION) AS AN ALTERNATIVE TO PRISON. TURING DIED IN 1954, JUST OVER TWO WEEKS BEFORE HIS 42ND BIRTHDAY, FROM CYANIDE POISONING. AN INQUEST DETERMINED THAT HIS DEATH WAS SUICIDE; HIS MOTHER AND SOME OTHERS BELIEVED HIS DEATH WAS ACCIDENTAL. ON 10 SEPTEMBER 2009, FOLLOWING AN INTERNET CAMPAIGN, BRITISH PRIME MINISTER GORDON BROWN MADE AN OFFICIAL PUBLIC APOLOGY ON BEHALF OF THE BRITISH GOVERNMENT FOR "THE APPALLING WAY HE WAS TREATED." AS OF MAY 2012 A PRIVATE MEMBER\'S BILL WAS BEFORE THE HOUSE OF LORDS WHICH WOULD GRANT TURING A STATUTORY PARDON IF ENACTED.\n'
  441 + expectedOutput = 'Determining most likely key lengths with Kasiski Examination...\nKasiski Examination results say the most likely key lengths are: 3 2 6 4 12 \n\nAttempting break with key length 3 (27 possible keys)...\nPossible letters for letter 1 of the key: A L M \nPossible letters for letter 2 of the key: S N O \nPossible letters for letter 3 of the key: V I Z \nAttempting with key: ASV\nAttempting with key: ASI\nAttempting with key: ASZ\nAttempting with key: ANV\nAttempting with key: ANI\nAttempting with key: ANZ\nAttempting with key: AOV\nAttempting with key: AOI\nAttempting with key: AOZ\nAttempting with key: LSV\nAttempting with key: LSI\nAttempting with key: LSZ\nAttempting with key: LNV\nAttempting with key: LNI\nAttempting with key: LNZ\nAttempting with key: LOV\nAttempting with key: LOI\nAttempting with key: LOZ\nAttempting with key: MSV\nAttempting with key: MSI\nAttempting with key: MSZ\nAttempting with key: MNV\nAttempting with key: MNI\nAttempting with key: MNZ\nAttempting with key: MOV\nAttempting with key: MOI\nAttempting with key: MOZ\nAttempting break with key length 2 (9 possible keys)...\nPossible letters for letter 1 of the key: O A E \nPossible letters for letter 2 of the key: M S I \nAttempting with key: OM\nAttempting with key: OS\nAttempting with key: OI\nAttempting with key: AM\nAttempting with key: AS\nAttempting with key: AI\nAttempting with key: EM\nAttempting with key: ES\nAttempting with key: EI\nAttempting break with key length 6 (729 possible keys)...\nPossible letters for letter 1 of the key: A E O \nPossible letters for letter 2 of the key: S D G \nPossible letters for letter 3 of the key: I V X \nPossible letters for letter 4 of the key: M Z Q \nPossible letters for letter 5 of the key: O B Z \nPossible letters for letter 6 of the key: V I K \nAttempting with key: ASIMOV\n\nPossible encryption break:\nKey ASIMOV: ALAN MATHISON TURING WAS A BRITISH MATHEMATICIAN, LOGICIAN, CRYPTANALYST, AND COMPUTER SCIENTIST. HE WAS HIGHLY INFLUENTIAL IN THE DEVELOPMENT OF COMPUTER SCIENCE, PROVIDING A FORMALISATION OF THE CON\n\nEnter D for done, or just press Enter to continue breaking:\n> Copying broken ciphertext to clipboard:\nALAN MATHISON TURING WAS A BRITISH MATHEMATICIAN, LOGICIAN, CRYPTANALYST, AND COMPUTER SCIENTIST. HE WAS HIGHLY INFLUENTIAL IN THE DEVELOPMENT OF COMPUTER SCIENCE, PROVIDING A FORMALISATION OF THE CONCEPTS OF "ALGORITHM" AND "COMPUTATION" WITH THE TURING MACHINE. TURING IS WIDELY CONSIDERED TO BE THE FATHER OF COMPUTER SCIENCE AND ARTIFICIAL INTELLIGENCE. DURING WORLD WAR II, TURING WORKED FOR THE GOVERNMENT CODE AND CYPHER SCHOOL (GCCS) AT BLETCHLEY PARK, BRITAIN\'S CODEBREAKING CENTRE. FOR A TIME HE WAS HEAD OF HUT 8, THE SECTION RESPONSIBLE FOR GERMAN NAVAL CRYPTANALYSIS. HE DEVISED A NUMBER OF TECHNIQUES FOR BREAKING GERMAN CIPHERS, INCLUDING THE METHOD OF THE BOMBE, AN ELECTROMECHANICAL MACHINE THAT COULD FIND SETTINGS FOR THE ENIGMA MACHINE. AFTER THE WAR HE WORKED AT THE NATIONAL PHYSICAL LABORATORY, WHERE HE CREATED ONE OF THE FIRST DESIGNS FOR A STORED-PROGRAM COMPUTER, THE ACE. IN 1948 TURING JOINED MAX NEWMAN\'S COMPUTING LABORATORY AT MANCHESTER UNIVERSITY, WHERE HE ASSISTED IN THE DEVELOPMENT OF THE MANCHESTER COMPUTERS AND BECAME INTERESTED IN MATHEMATICAL BIOLOGY. HE WROTE A PAPER ON THE CHEMICAL BASIS OF MORPHOGENESIS, AND PREDICTED OSCILLATING CHEMICAL REACTIONS SUCH AS THE BELOUSOV-ZHABOTINSKY REACTION, WHICH WERE FIRST OBSERVED IN THE 1960S. TURING\'S HOMOSEXUALITY RESULTED IN A CRIMINAL PROSECUTION IN 1952, WHEN HOMOSEXUAL ACTS WERE STILL ILLEGAL IN THE UNITED KINGDOM. HE ACCEPTED TREATMENT WITH FEMALE HORMONES (CHEMICAL CASTRATION) AS AN ALTERNATIVE TO PRISON. TURING DIED IN 1954, JUST OVER TWO WEEKS BEFORE HIS 42ND BIRTHDAY, FROM CYANIDE POISONING. AN INQUEST DETERMINED THAT HIS DEATH WAS SUICIDE; HIS MOTHER AND SOME OTHERS BELIEVED HIS DEATH WAS ACCIDENTAL. ON 10 SEPTEMBER 2009, FOLLOWING AN INTERNET CAMPAIGN, BRITISH PRIME MINISTER GORDON BROWN MADE AN OFFICIAL PUBLIC APOLOGY ON BEHALF OF THE BRITISH GOVERNMENT FOR "THE APPALLING WAY HE WAS TREATED." AS OF MAY 2012 A PRIVATE MEMBER\'S BILL WAS BEFORE THE HOUSE OF LORDS WHICH WOULD GRANT TURING A STATUTORY PARDON IF ENACTED.\n'
445 442
446 443 self.assertEqual(pyperclip.paste().decode('ascii'), expectedClipboard)
447 444 self.assertEqual(procOut, expectedOutput)
448 445
449 446
450   - def test_freqFinderProgram(self):
451   - proc = subprocess.Popen('c:\\python32\\python.exe freqFinder.py', stdout=subprocess.PIPE, stdin=subprocess.PIPE)
452   - procOut = proc.communicate()[0].decode('ascii')
453   -
454   - expectedOutput = '12\nShakespeare\'s Sonnet #29\nWHEN, IN DISGRACE WITH FORTUNE AND MEN\'S EYES,\nI ALL ALONE BEWEEP MY OUTCAST STATE,\nAND TROUBLE DEAF HEAVEN WITH MY BOOTLESS CRIES,\nAND LOOK UPON MYSELF AND CURSE MY FATE,\nWISHING ME LIKE TO ONE MORE RICH IN HOPE,\nFEATURED LIKE HIM, LIKE HIM WITH FRIENDS POSSESSED,\nDESIRING THIS MAN\'S ART, AND THAT MAN\'S SCOPE,\nWITH WHAT I MOST ENJOY CONTENTED LEAST,\nYET IN THESE THOUGHTS MYSELF ALMOST DESPISING,\nHAPLY I THINK ON THEE, AND THEN MY STATE,\nLIKE TO THE LARK AT BREAK OF DAY ARISING\nFROM SULLEN EARTH, SINGS HYMNS AT HEAVEN\'S GATE\n\nFOR THY SWEET LOVE REMEMBERED SUCH WEALTH BRINGS,\nTHAT THEN I SCORN TO CHANGE MY STATE WITH KINGS.\n\nLetter Frequencies of Sonnet #29:\n{\'A\': 37, \'C\': 10, \'B\': 6, \'E\': 66, \'D\': 16, \'G\': 11, \'F\': 10, \'I\': 35, \'H\': 32, \'K\': 9, \'J\': 1, \'M\': 20, \'L\': 20, \'O\': 28, \'N\': 38, \'Q\': 0, \'P\': 7, \'S\': 42, \'R\': 21, \'U\': 9, \'T\': 49, \'W\': 11, \'V\': 3, \'Y\': 14, \'X\': 0, \'Z\': 0}\n\nFrequency score of Sonnet #29:\n10\n\nFrequency score of Scrambled Sonnet #29:\n10\n\nFrequency score of Lorem Ipsum text:\n4\n\nFrequency score of alphabet:\n0\n\nFrequency score of alphabet x 100:\n0\n\nFrequency score of "AAAAAAAAAAAAAAAH":\n1\n\nFrequency score of "VDIUFRFDSFEWAFDSAFLKHFDSALKFA":\n1\n\n'
455   -
456   - self.assertEqual(procOut, expectedOutput)
457   -
458   -
459   - def test_primeSieveProgram(self):
460   - proc = subprocess.Popen('c:\\python32\\python.exe primeSieve.py', stdout=subprocess.PIPE, stdin=subprocess.PIPE)
461   - procOut = proc.communicate()[0].decode('ascii')
462   -
463   - expectedOutput = ' 2 is prime: True\n 5 is prime: True\n 11 is prime: True\n 16 is prime: False\n 17 is prime: True\n101 is prime: True\n126 is prime: False\n147 is prime: False\n\n 2 is prime: True\n 5 is prime: True\n 11 is prime: True\n 16 is prime: False\n 17 is prime: True\n101 is prime: True\n126 is prime: False\n147 is prime: False\n\nTesting if both functions are consistent with each other...\nTest Passed: Both functions are consistent with each other.\n'
464   - self.assertEqual(procOut, expectedOutput)
465   -
466 447 def test_primeSieveModule(self):
467 448 import primeSieve
468 449
@@ -608,7 +589,7 @@ def test_vigenereCipherProgram(self):
608 589
609 590 if not TEST_ALL:
610 591 customSuite = unittest.TestSuite()
611   - customSuite.addTest(CodeBreakerUnitTests('test_primeSieveModule'))
  592 + customSuite.addTest(CodeBreakerUnitTests('test_vigenereBreakerProgram'))
612 593 unittest.TextTestRunner().run(customSuite)
613 594 elif TEST_ALL:
614 595 unittest.main()
25 detectEnglish.py
@@ -24,17 +24,6 @@ def loadDictionary(dictionaryFilename):
24 24 ENGLISH_WORDS = loadDictionary('dictionary.txt')
25 25
26 26
27   -def main():
28   - print('Testing the English detection module...')
29   - messages = ['The quick brown fox jumped over the yellow lazy dog.',
30   - 'Hello there. lkjjfldsf dsafk alf ewfewlfjl efa',
31   - 'Sumimasen. Kore wa nan desu ka?',
32   - '1100010110010111001011110000']
33   - for m in messages:
34   - print('%s\n\t%s\n' % (m, isEnglish(m)))
35   -
36   -
37   -# The getEnglishCount() function's code was copy/pasted from transpositionBreaker.py
38 27 def getEnglishCount(message):
39 28 message = message.upper()
40 29 message = re.sub('[^A-Z\s]', '', message)
@@ -51,9 +40,9 @@ def getEnglishCount(message):
51 40
52 41
53 42 def isEnglish(message, wordPercentage=20, letterPercentage=67):
54   - # By default, 20% of the words must be recognized as English words that
55   - # exist in the dictionary file, and 67% of all the characters in the
56   - # message must be letters (not punctuation, spaces, or numbers).
  43 + # By default, 20% of the words must exist in the dictionary file, and
  44 + # 67% of all the characters in the message must be letters (not
  45 + # punctuation, spaces, or numbers).
57 46 wordPercentage /= 100
58 47 letterPercentage /= 100
59 48
@@ -66,10 +55,4 @@ def isEnglish(message, wordPercentage=20, letterPercentage=67):
66 55 if symbol in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
67 56 numLetters += 1
68 57
69   - return (englishWords >= wordPercentage) and (numLetters / len(message) >= letterPercentage)
70   -
71   -
72   -# If detectEnglish.py is run (instead of imported as a module) call
73   -# the main() function.
74   -if __name__ == '__main__':
75   - main()
  58 + return (englishWords >= wordPercentage) and (numLetters / len(message) >= letterPercentage)
100 freqFinder.py
@@ -3,7 +3,7 @@
3 3
4 4 # frequency taken from http://en.wikipedia.org/wiki/Letter_frequency
5 5 englishLetterFreq = {'E': 12.70, 'T': 9.06, 'A': 8.17, 'O': 7.51, 'I': 6.97, 'N': 6.75, 'S': 6.33, 'H': 6.09, 'R': 5.99, 'D': 4.25, 'L': 4.03, 'C': 2.78, 'U': 2.76, 'M': 2.41, 'W': 2.36, 'F': 2.23, 'G': 2.02, 'Y': 1.97, 'P': 1.93, 'B': 1.29, 'V': 0.98, 'K': 0.77, 'J': 0.15, 'X': 0.15, 'Q': 0.10, 'Z': 0.07}
6   -englishTrigramFreq = {'THE': 3.508232, 'AND': 1.593878, 'ING': 1.147042, 'HER': 0.822444, 'HAT': 0.650715, 'HIS': 0.596748, 'THA': 0.593593, 'ERE': 0.560594, 'FOR': 0.555372, 'ENT': 0.530771, 'ION': 0.506454, 'TER': 0.461099, 'WAS': 0.460487, 'YOU': 0.437213, 'ITH': 0.43125, 'VER': 0.430732, 'ALL': 0.422758, 'WIT': 0.39729, 'THI': 0.394796, 'TIO': 0.378058}
  6 +englishTrigramFreq = {'THE': 3.51, 'AND': 1.59, 'ING': 1.15, 'HER': 0.82, 'HAT': 0.65, 'HIS': 0.60, 'THA': 0.59, 'ERE': 0.56, 'FOR': 0.56, 'ENT': 0.53, 'ION': 0.51, 'TER': 0.46, 'WAS': 0.46, 'YOU': 0.44, 'ITH': 0.43, 'VER': 0.43, 'ALL': 0.42, 'WIT': 0.40, 'THI': 0.39, 'TIO': 0.38}
7 7 englishFreqOrder = tuple('ETAOINSHRDLCUMWFGYPBVKJXQZ')
8 8 ETAOIN = ''.join(englishFreqOrder)
9 9 LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
@@ -11,70 +11,6 @@
11 11 TRIGRAM_THRESHOLD = 2
12 12 TRIGRAM_MATCH_RANGE = 30
13 13
14   -def main():
15   - import random
16   -
17   - sonnet = """When, in disgrace with Fortune and men's eyes,
18   -I all alone beweep my outcast state,
19   -And trouble deaf heaven with my bootless cries,
20   -And look upon myself and curse my fate,
21   -Wishing me like to one more rich in hope,
22   -Featured like him, like him with friends possessed,
23   -Desiring this man's art, and that man's scope,
24   -With what I most enjoy contented least,
25   -Yet in these thoughts myself almost despising,
26   -Haply I think on thee, and then my state,
27   -Like to the lark at break of day arising
28   -From sullen earth, sings hymns at heaven's gate
29   -
30   -For thy sweet love remembered such wealth brings,
31   -That then I scorn to change my state with kings.""".upper()
32   -
33   - loremIpsum = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam porta varius ante eget tincidunt. Pellentesque placerat, turpis nec elementum consectetur, enim nulla molestie velit, quis semper erat lacus quis metus.'.upper()
34   -
35   - print(englishFreqMatch("""Originally conceived to follow the UK Prestel specifications, and developed on contract by IBM Germany, Btx added a number of additional features before launch, including some inspired by the French Minitel service, to create a new display standard of its own, which in 1981 was designated the CEPT1 profile. In 1995 an enhanced backward-compatible standard called Kernel for Intelligent Communication Terminals (KIT) was announced, but this never really gained acceptance. CEPT permits the transmission of graphical pages with a resolution of 480 by 250 pixels, where 32 out of a palette of 4096 colors could be shown at the same time. This corresponds to the technical possibilities of the early 1980s."""))
36   -
37   - print('Shakespeare\'s Sonnet #29')
38   - print(sonnet)
39   - print()
40   -
41   - print('Letter Frequencies of Sonnet #29:')
42   - print(getLetterCount(sonnet))
43   - print()
44   -
45   - print('Frequency score of Sonnet #29:')
46   - print(englishFreqMatch(sonnet))
47   - print()
48   -
49   - # Scrambling Sonnet #29
50   - scrambled = list(sonnet)
51   - random.shuffle(scrambled)
52   - scrambled = ''.join(scrambled)
53   -
54   - print('Frequency score of Scrambled Sonnet #29:')
55   - print(englishFreqMatch(scrambled))
56   - print()
57   -
58   - print('Frequency score of Lorem Ipsum text:')
59   - print(englishFreqMatch(loremIpsum))
60   - print()
61   -
62   - print('Frequency score of alphabet:')
63   - print(englishFreqMatch(LETTERS))
64   - print()
65   -
66   - print('Frequency score of alphabet x 100:')
67   - print(englishFreqMatch(LETTERS * 100))
68   - print()
69   -
70   - print('Frequency score of "AAAAAAAAAAAAAAAH":')
71   - print(englishFreqMatch("AAAAAAAAAAAAAAAH"))
72   - print()
73   -
74   - print('Frequency score of "VDIUFRFDSFEWAFDSAFLKHFDSALKFA":')
75   - print(englishFreqMatch("VDIUFRFDSFEWAFDSAFLKHFDSALKFA"))
76   - print()
77   -
78 14
79 15 def getLetterCount(message):
80 16 # Returns a dictionary with keys of single letters and values of the
@@ -109,27 +45,31 @@ def getFrequencyOrder(message):
109 45 # frequently occurring in the message parameter.
110 46 message = message.upper()
111 47
112   - # first, get a dictionary of each letter and its frequency count from the message
  48 + # first, get a dictionary of each letter and its frequency count
113 49 letterToFreq = getLetterCount(message)
114 50
115   - # second, make a dictionary of each frequency count to each letter(s) with that frequency
  51 + # second, make a dictionary of each frequency count to each letter(s)
  52 + # with that frequency
116 53 freqToLetter = {}
117 54 for letter in LETTERS:
118   - freqToLetter[letterToFreq[letter]] = [] # intialize to a blank list
  55 + freqToLetter[letterToFreq[letter]] = [] # start as a blank list
119 56
120 57 for letter in LETTERS:
121 58 freqToLetter[letterToFreq[letter]].append(letter)
122 59
123   - # third, put each list of letters in reverse "ETAOIN" order, and then convert it to a string
  60 + # third, put each list of letters in reverse "ETAOIN" order, and then
  61 + # convert it to a string
124 62 for freq in freqToLetter:
125 63 freqToLetter[freq].sort(key=ETAOIN.find, reverse=True)
126 64 freqToLetter[freq] = ''.join(freqToLetter[freq])
127 65
128   - # fourth, convert the freqToLetter dictionary to a list of tuple pairs (key, value), then sort them
  66 + # fourth, convert the freqToLetter dictionary to a list of tuple
  67 + # pairs (key, value), then sort them
129 68 freqPairs = list(freqToLetter.items())
130 69 freqPairs.sort(key=lambda x: x[0], reverse=True)
131 70
132   - # fifth, now that the letters are ordered by frequency, extract all the letters for the final string
  71 + # fifth, now that the letters are ordered by frequency, extract all
  72 + # the letters for the final string
133 73 freqOrder = ''
134 74 for freqPair in freqPairs:
135 75 freqOrder += freqPair[1]
@@ -138,11 +78,11 @@ def getFrequencyOrder(message):
138 78
139 79
140 80 def englishFreqMatch(message):
141   - # Return the number of matches that the string in the message parameter
142   - # has when its letter frequency is compared to English letter frequency.
143   - # A "match" is how many of its six most frequent and six least frequent
144   - # letters is among the six most frequent and six least frequent letters
145   - # for English.
  81 + # Return the number of matches that the string in the message
  82 + # parameter has when its letter frequency is compared to English
  83 + # letter frequency. A "match" is how many of its six most frequent
  84 + # and six least frequent letters is among the six most frequent and
  85 + # six least frequent letters for English.
146 86 freqOrder = getFrequencyOrder(message)
147 87
148 88 matches = 0
@@ -197,10 +137,4 @@ def englishTrigramMatch(message):
197 137 if commonTrig in topFreqLetters[:TRIGRAM_MATCH_RANGE]:
198 138 matches += 1
199 139
200   - return matches >= TRIGRAM_THRESHOLD
201   -
202   -
203   -# If freqFinder.py is run (instead of imported as a module) call
204   -# the main() function.
205   -if __name__ == '__main__':
206   - main()
  140 + return matches >= TRIGRAM_THRESHOLD
1  makeRsaKeys.py
@@ -83,7 +83,6 @@ def generateKey(keySize=DEFAULT_KEY_SIZE):
83 83 return (publicKey, privateKey)
84 84
85 85
86   -
87 86 # If makeRsaKeys.py is run (instead of imported as a module) call
88 87 # the main() function.
89 88 if __name__ == '__main__':
49 nullBreaker.py
@@ -3,41 +3,54 @@
3 3
4 4 import nullCipher, pyperclip, detectEnglish, itertools
5 5
6   -# There are two settings our breaking program needs to limit the range of the possible keys it checks.
7   -# MAX_KEY_NUMBER is the range of numbers it checks for each number in the key. A MAX_KEY_NUMBER value of 9 means it will check the numbers 0 through 9.
8   -# MAX_KEY_LENGTH is the largest amount of numbers in the key. A MAX_KEY_LENGTH value of 5 means that the key could be something like '1 2 3 4 5' or '1 1 1 1 1' or '1 2 3 4', but not '1 2 3 4 5 6'
9   -# If these numbers are too large, then breaking the code will take a long time. If these numbers are too small, then the breaking program won't be able to break the encryption.
  6 +# There are two settings our breaking program needs to limit the range of
  7 +# the possible keys it checks.
  8 +# MAX_KEY_NUMBER is the range of numbers it checks for each number in the
  9 +# key. A MAX_KEY_NUMBER value of 9 means it will check 0 through 9.
  10 +# MAX_KEY_DIGITS is the largest amount of numbers in the key. A value of 5
  11 +# means that the key could be something like '1 2 3 4 5' or '1 1 1 1 1' or
  12 +# '1 2 3 4', but not '1 2 3 4 5 6'
  13 +# If these numbers are too large, then breaking the code will take a long
  14 +# time. If these numbers are too small, then the breaking program won't be
  15 +# able to break the encryption.
  16 +
10 17 MAX_KEY_NUMBER = 9
11   -MAX_KEY_LENGTH = 5
  18 +MAX_KEY_DIGITS = 5
12 19
13 20 SILENT_MODE = False
14 21
  22 +# This can be copy/pasted from http://invpy.com/nullBreaker.py
15 23 myMessage = """y\ZWh,De,. n #{ItZ9 uL<sl6!e 2&a"\B w{Eo;l#rdvK,9\s i.Xt?WC mQ-ef>yanpushOz j9lu_H4stsd .Bawhmua_ogt <`I#w$ ctoh({'oo={sINUe 84i%G3t NLt2#Wo 7Zm*<^eacUnuG6 -=g-f:! nxQe$Qmit&Ah0#ner:O Gt!moc;rXGUe q/nKrgor"\ 9 \lecBs|10s.9i"""
16   -# Wh e n I u s e a w o rd , i t m e an s j u st wh a t I c h oo s e i t t o m ea n - - n e it h er mo r e n or le s s.
  24 +
17 25
18 26 def main():
19   - # As a convenience to the user, we will calculate the number of keys that the current MAX_KEY_LENGTH and MAX_KEY_NUMBER settings will cause the breaker program to go through.
  27 + # Calculate the number of keys that the current MAX_KEY_DIGITS and
  28 + # MAX_KEY_NUMBER values will cause the breaker program to go through.
20 29 possibleKeys = 0 # start the number of keys at 0.
21   - for i in range(1, MAX_KEY_LENGTH + 1):
22   - # In order to find the total number of possible keys, we need to add the total number of keys of 1 number, of 2 numbers, of 3 numbers, and so on up to MAX_KEY_LENGTH numbers.
23   - # To find the number of keys with i numbers in them, we multiply the range of numbers (that is, MAX_KEY_NUMBER) by itself i times. That is, we find MAX_KEY_NUMBER to the ith power.
  30 + for i in range(1, MAX_KEY_DIGITS + 1):
  31 + # To find the total number of possible keys, add the total number
  32 + # of keys for 1-digit keys, 2-digit keys, and so on up to
  33 + # MAX_KEY_DIGITS-digit keys.
  34 + # To find the number of keys with i digits in them, multiply the
  35 + # range of numbers (that is, MAX_KEY_NUMBER) by itself i times.
  36 + # That is, we find MAX_KEY_NUMBER to the ith power.
24 37 possibleKeys += MAX_KEY_NUMBER ** i
25 38
26   - # After exiting the loop, the value in possibleKeys is the total number of keys for MAX_KEY_NUMBER and MAX_KEY_RANGE. We then print this data to the user.
  39 + # After exiting the loop, the value in possibleKeys is the total number
  40 + # of keys for MAX_KEY_NUMBER and MAX_KEY_RANGE.
27 41 print('Max key number: %s' % MAX_KEY_NUMBER)
28   - print('Max key length: %s' % MAX_KEY_LENGTH)
  42 + print('Max key length: %s' % MAX_KEY_DIGITS)
29 43 print('Possible keys to try: %s' % (possibleKeys))
30 44 print()
31 45
32   - # Python programs can be stopped at any time by pressing Ctrl-C (on Windows) or Ctrl-D (on Mac and Linux)
  46 + # Python programs can be stopped at any time by pressing Ctrl-C (on
  47 + # Windows) or Ctrl-D (on Mac and Linux)
33 48 print('(Press Ctrl-C or Ctrl-D to quit at any time.)')
34 49 print('Breaking...')
35 50
36   - # The breakNull() function will have all the encryption breaking code in it, and return the original plaintext.
37 51 brokenMessage = breakNull(myMessage)
38 52
39 53 if brokenMessage != None:
40   - # The plaintext is displayed on the screen. For the convenience of the user, we copy the text of the code to the clipboard.
41 54 print('Copying broken message to clipboard:')
42 55 print(brokenMessage)
43 56 pyperclip.copy(brokenMessage)
@@ -46,9 +59,9 @@ def main():
46 59
47 60
48 61 def breakNull(ciphertext):
49   - # The program needs to try keys of length 1 (such as '5'), of length 2 (such as '5 5'), and so on up to length MAX_KEY_LENGTH.
50   - # This is because the key '1 0' will decrypt differently than '1 0 0'.
51   - for keyLength in range(1, MAX_KEY_LENGTH + 1):
  62 + # The program needs to try keys of length 1 (such as '5'), of length 2
  63 + # (such as '5 3'), and so on up to length MAX_KEY_DIGITS.
  64 + for keyLength in range(1, MAX_KEY_DIGITS + 1):
52 65 for keyParts in itertools.product(range(MAX_KEY_NUMBER + 1), repeat=keyLength):
53 66 key = []
54 67 for digit in keyParts:
49 primeSieve.py
@@ -3,33 +3,11 @@
3 3
4 4 import math
5 5
6   -def main():
7   - print(' 2 is prime: %s' % (isPrime(2)))
8   - print(' 5 is prime: %s' % (isPrime(5)))
9   - print(' 11 is prime: %s' % (isPrime(11)))
10   - print(' 16 is prime: %s' % (isPrime(16)))
11   - print(' 17 is prime: %s' % (isPrime(17)))
12   - print('101 is prime: %s' % (isPrime(101)))
13   - print('126 is prime: %s' % (isPrime(126)))
14   - print('147 is prime: %s' % (isPrime(147)))
15   - print()
16   - primes = primeSieve(1000)
17   - print(' 2 is prime: %s' % (2 in primes))
18   - print(' 5 is prime: %s' % (5 in primes))
19   - print(' 11 is prime: %s' % (11 in primes))
20   - print(' 16 is prime: %s' % (16 in primes))
21   - print(' 17 is prime: %s' % (17 in primes))
22   - print('101 is prime: %s' % (101 in primes))
23   - print('126 is prime: %s' % (126 in primes))
24   - print('147 is prime: %s' % (147 in primes))
25   - print()
26   - _testPrimeFunctions()
27   -
28 6
29 7 def isPrime(num):
30 8 # Returns True if num is a prime number, otherwise False.
31 9
32   - # Note: Generally, isPrime() is slower than primeSieve()
  10 + # Note: Generally, isPrime() is slower than primeSieve().
33 11
34 12 # all numbers less than 2 are not prime
35 13 if num < 2:
@@ -63,27 +41,4 @@ def primeSieve(sieveSize):
63 41 if sieve[i] == True:
64 42 primes.append(i)
65 43
66   - return primes
67   -
68   -
69   -def _testPrimeFunctions():
70   - TEST_SIZE = 20000
71   - sievePrimes = primeSieve(TEST_SIZE)
72   -
73   - print('Testing if both functions are consistent with each other...')
74   - allCorrect = True
75   - for i in range(TEST_SIZE):
76   - if (i in sievePrimes and not isPrime(i)) or (i not in sievePrimes and isPrime(i)):
77   - print('The two functions disagree if %s is prime.' % (i))
78   - allCorrect = False
79   -
80   - if allCorrect:
81   - print('Test Passed: Both functions are consistent with each other.')
82   - else:
83   - print('ERROR! Both functions are not consistent with each other.')
84   -
85   -
86   -# If primeSieve.py is run (instead of imported as a module) call
87   -# the main() function.
88   -if __name__ == '__main__':
89   - main()
  44 + return primes
2  rabinMiller.py
@@ -20,7 +20,7 @@ def rabinMiller(num):
20 20 s = s // 2
21 21 t += 1
22 22
23   - for dummy_trials in range(5): # attempt to falsify num's primality 5 times
  23 + for dummy_trials in range(5): # try to falsify num's primality 5 times
24 24 a = random.randrange(2, num - 1)
25 25 v = pow(a, s, num)
26 26 if v != 1: # this test does not apply if v is 1.
15 rsaCipher.py
@@ -4,7 +4,8 @@
4 4 import sys
5 5
6 6 # IMPORTANT: The block size MUST be less or equal than the key size!
7   -# (Note: The block size is in bytes, the key size is in bits. There are 8 bits in 1 byte.)
  7 +# (Note: The block size is in bytes, the key size is in bits. There
  8 +# are 8 bits in 1 byte.)
8 9 DEFAULT_BLOCK_SIZE = 128
9 10 BYTE_SIZE = 256 # One byte has 256 different values.
10 11
@@ -48,17 +49,17 @@ def getBlocksFromText(message, blockSize=DEFAULT_BLOCK_SIZE):
48 49
49 50
50 51 def getTextFromBlocks(blockInts, messageLength, blockSize=DEFAULT_BLOCK_SIZE):
51   - # Converts a list of block integers to the original message string. The
52   - # original message length is needed to properly convert the last block
53   - # integer.
  52 + # Converts a list of block integers to the original message string.
  53 + # The original message length is needed to properly convert the last
  54 + # block integer.
54 55 message = []
55 56
56 57 for blockInt in blockInts:
57 58 blockMessage = []
58 59 for i in range(blockSize-1, -1, -1):
59 60 if len(message) + i < messageLength:
60   - # Decode the message string for the 512 (or whatever blockSize is
61   - # set to) characters from this block integer.
  61 + # Decode the message string for the 512 (or whatever
  62 + # blockSize is set to) characters from this block integer.
62 63 charNumber = blockInt // (BYTE_SIZE ** i)
63 64 blockInt = blockInt % (BYTE_SIZE ** i)
64 65 blockMessage.insert(0, bytes([charNumber]).decode('ascii'))
@@ -68,7 +69,7 @@ def getTextFromBlocks(blockInts, messageLength, blockSize=DEFAULT_BLOCK_SIZE):
68 69
69 70 def encryptMessage(message, key, blockSize=DEFAULT_BLOCK_SIZE):
70 71 # Converts the message string into a list of block integers, and then
71   - # encrypt each block integer. Be sure to pass the PUBLIC key to encrypt.
  72 + # encrypt each block integer. Pass the PUBLIC key to encrypt.
72 73 encryptedBlocks = []
73 74 n, e = key
74 75
82 simpleSubBreaker.py
@@ -3,17 +3,17 @@
3 3
4 4 """
5 5 In this program, a "word pattern" is a description of which letters are
6   -repeated in a word. A word pattern is a series of numbers delimited by periods.
7   -The first letter to appear in the word is assigned 0, the second letter 1, and
8   -so on. So the word pattern for 'cucumber' is '0.1.0.1.2.3.4.5' because the
9   -first letter 'c' occurs as the first and third letter in the word 'cucumber'.
10   -So the pattern has '0' as the first and third number.
  6 +repeated in a word. A word pattern is numbers delimited by periods.
  7 +The first letter to appear in the word is assigned 0, the second letter 1,
  8 +and so on. So the word pattern for 'cucumber' is '0.1.0.1.2.3.4.5' because the
  9 +first letter 'c' occurs as the first and third letter in the word
  10 +'cucumber'. So the pattern has '0' as the first and third number.
11 11
12 12 The pattern for 'abc' or 'cba' is '0.1.2'
13 13 The pattern for 'aaa' or 'bbb' is '0.0.0'
14 14 The pattern for 'hello' is '0.1.2.2.3'
15   -The pattern for 'advise' or 'closet' is '0.1.2.3.4.5' (they have only unique
16   -letters in the word)
  15 +The pattern for 'advise' or 'closet' is '0.1.2.3.4.5' (they have only
  16 +unique letters in the word)
17 17
18 18 In this program, a "candidate" is a possible English word that a ciphertext
19 19 work can decrypt to.
@@ -21,10 +21,10 @@
21 21 ciphertext word 'JHJHWDOV' (because all of them have the pattern
22 22 '0.1.0.1.2.3.4.5')
23 23
24   -In this program, a "map" or "mapping" is a dictionary where the keys are the
25   -letters in LETTERS (e.g. 'A', 'B', 'C', etc) and the values are lists of
26   -letters that could possibly be the correct decryption. If the list is blank,
27   -this means that it is unknown what this letter could decrypt to.
  24 +In this program, a "map" or "mapping" is a dictionary where the keys are
  25 +the letters in LETTERS (e.g. 'A', 'B', 'C', etc) and the values are lists
  26 +of letters that could possibly be the correct decryption. If the list is
  27 +blank, this means that it is unknown what this letter could decrypt to.
28 28 """
29 29
30 30 # The import statement for wordPatterns is further down.
@@ -77,7 +77,7 @@ def main():
77 77
78 78 def getWordPattern(word):
79 79 # Returns a string of the pattern form of the given word.
80   - # e.g. '0.1.2.3.4.1.2.3.5.6' for 'DUSTBUSTER' or '0.1.2.2.3' for 'DOGGY'
  80 + # e.g. '0.1.2.3.4.1.2.3.5.6' for 'DUSTBUSTER'
81 81 word = word.upper()
82 82 nextNum = 0
83 83 letterNums = {}
@@ -129,14 +129,14 @@ def getNewBlankMapping():
129 129
130 130
131 131 def addMappings(theMap, cipherWord, candidate):
132   - # The theMap parameter is a "mapping" data structure that this function
133   - # modifies. (See the comments at the top of this file.)
  132 + # The theMap parameter is a "mapping" data structure that this
  133 + # function modifies. (See the comments at the top of this file.)
134 134 # The cipherWord parameter is a string value of the ciphertext word.
135   - # The candidate parameter is a possible English word that the cipherWord
136   - # could decrypt to.
  135 + # The candidate parameter is a possible English word that the
  136 + # cipherWord could decrypt to.
137 137
138   - # This function modifies theMap so that the mappings of the cipherWord's
139   - # letters to the candidate's letters are added to theMap.
  138 + # This function modifies theMap so that the mappings of the
  139 + # cipherWord's letters to the candidate's letters are added to theMap.
140 140
141 141 for i in range(len(cipherWord)):
142 142 if candidate[i] not in theMap[cipherWord[i]]:
@@ -150,8 +150,8 @@ def intersectMappings(mapA, mapB):
150 150 result = getNewBlankMapping()
151 151 for i in mapA.keys():
152 152
153   - # An empty list means "any letter is possible". So just copy the other
154   - # map entirely.
  153 + # An empty list means "any letter is possible". So just copy the
  154 + # other map entirely.
155 155 if mapA[i] == []:
156 156 result[i] = copy.copy(mapB[i])
157 157 elif mapB[i] == []:
@@ -165,14 +165,14 @@ def intersectMappings(mapA, mapB):
165 165
166 166
167 167 def removeSolvedLettersFromMapping(theMap):
168   - # Letters in the mapping that map to only one letter are consider "solved"
169   - # and can be removed from the other letters.
170   - # For example, if 'A' maps to possible letters ['M', 'N'], and 'B' maps
171   - # to ['N'], then we know that 'B' must map to 'N', so we can remove 'N'
172   - # from the list of what 'A' could map to. So 'A' then maps to ['M'].
173   - # Note that now that 'A' maps to only one letter, we can remove 'M'
174   - # from the list of possible mappings for every other letter. (This is why
175   - # there is a loop that keeps reducing the map.)
  168 + # Letters in the mapping that map to only one letter are consider
  169 + # "solved" and can be removed from the other letters.
  170 + # For example, if 'A' maps to possible letters ['M', 'N'], and 'B'
  171 + # maps to ['N'], then we know that 'B' must map to 'N', so we can
  172 + # remove 'N' from the list of what 'A' could map to. So 'A' then maps
  173 + # to ['M']. Note that now that 'A' maps to only one letter, we can
  174 + # remove 'M' from the list of possible mappings for every other
  175 + # letter. (This is why there is a loop that keeps reducing the map.)
176 176 previousSolvedLetters = []
177 177 solvedLetters = None
178 178 while previousSolvedLetters != solvedLetters:
@@ -181,24 +181,24 @@ def removeSolvedLettersFromMapping(theMap):
181 181 previousSolvedLetters = solvedLetters
182 182 solvedLetters = []
183 183
184   - # solvedLetters will be a list of English letters that have one and
185   - # only one possible mapping in theMap
  184 + # solvedLetters will be a list of English letters that have one
  185 + # and only one possible mapping in theMap
186 186 for i in theMap:
187 187 if len(theMap[i]) == 1:
188 188 solvedLetters.append(theMap[i][0])
189 189
190 190 # If a letter is solved, than it cannot possibly be a possible
191   - # decryption letter for a different ciphertext letter, so we should
192   - # remove it.
  191 + # decryption letter for a different ciphertext letter, so we
  192 + # should remove it.
193 193 for i in theMap:
194 194 for s in solvedLetters:
195 195 if len(theMap[i]) != 1 and s in theMap[i]:
196 196 theMap[i].remove(s)
197 197
198   - # With a letter removed, it's possible that we may have reduced other
199   - # ciphertext letters to one and only one solution, so keep looping
200   - # until previousSolvedLetters == solvedLetters. At that point, we'll
201   - # know we can't rmemove any more letters.
  198 + # With a letter removed, it's possible that we may have reduced
  199 + # other ciphertext letters to one and only one solution, so keep
  200 + # looping until previousSolvedLetters == solvedLetters. At that
  201 + # point, we'll know we can't rmemove any more letters.
202 202 return theMap
203 203
204 204
@@ -232,7 +232,7 @@ def decryptWithMap(ciphertext, theMap):
232 232 key = ['x'] * len(LETTERS)
233 233 for letter in theMap.keys():
234 234 if len(theMap[letter]) == 1:
235   - # There is only one possible letter mapping, so add it to the key.
  235 + # If only one possible letter mapping, add it to the key.
236 236 keyIndex = LETTERS.find(theMap[letter][0].upper())
237 237 key[keyIndex] = letter.upper()
238 238 else:
@@ -244,9 +244,9 @@ def decryptWithMap(ciphertext, theMap):
244 244 return simpleSubCipher.decryptMessage(key, ciphertext)
245 245
246 246 def checkForWordPatternsPy():
247   - # If the wordPatterns.py file does not exist, create it based on the words
248   - # in our dictionary text file, dictionary.txt.
249   - # (You can download this file from http://inventwithpython.com/dictionary.txt)
  247 + # If the wordPatterns.py file does not exist, create it based on the
  248 + # words in our dictionary text file, dictionary.txt.
  249 + # (Download this file from http://invpy.com/dictionary.txt)
250 250 if not os.path.exists('wordPatterns.py'):
251 251 import pprint # import the "pretty print" module
252 252 allPatterns = {}
@@ -256,7 +256,7 @@ def checkForWordPatternsPy():
256 256 fp.close()
257 257
258 258 for word in wordList:
259   - word = word.strip() # get rid of newline at the end of the string
  259 + word = word.strip() # get rid of newline at the end
260 260 pattern = getWordPattern(word)
261 261
262 262 if pattern not in allPatterns:
6 transpositionBreaker.py
@@ -5,7 +5,7 @@
5 5
6 6 def main():
7 7 # You might want to copy & paste this text from the source code at
8   - # http://inventwithpython.com/transpositionBreaker.py
  8 + # http://invpy.com/transpositionBreaker.py
9 9 myMessage = """Cb b rssti aieih rooaopbrtnsceee er es no npfgcwu plri ch nitaalr eiuengiteehb(e1 hilincegeoamn fubehgtarndcstudmd nM eu eacBoltaeteeoinebcdkyremdteghn.aa2r81a condari fmps" tad l t oisn sit u1rnd stara nvhn fsedbh ee,n e necrg6 8nmisv l nc muiftegiitm tutmg cm shSs9fcie ebintcaets h aihda cctrhe ele 1O7 aaoem waoaatdahretnhechaopnooeapece9etfncdbgsoeb uuteitgna.rteoh add e,D7c1Etnpneehtn beete" evecoal lsfmcrl iu1cifgo ai. sl1rchdnheev sh meBd ies e9t)nh,htcnoecplrrh ,ide hmtlme. pheaLem,toeinfgn t e9yce da' eN eMp a ffn Fc1o ge eohg dere.eec s nfap yox hla yon. lnrnsreaBoa t,e eitsw il ulpbdofgBRe bwlmprraio po droB wtinue r Pieno nc ayieeto'lulcih sfnc ownaSserbereiaSm-eaiah, nnrttgcC maciiritvledastinideI nn rms iehn tsigaBmuoetcetias rn"""
10 10
11 11 brokenCiphertext = breakTransposition(myMessage)
@@ -14,7 +14,7 @@ def main():
14 14 print('Failed to break encryption.')
15 15 else:
16 16 print('Copying broken ciphertext to clipboard:')
17   - print(brokenCiphertext[:1000]) # only print the first 1000 characters
  17 + print(brokenCiphertext)
18 18 pyperclip.copy(brokenCiphertext)
19 19
20 20
@@ -32,7 +32,7 @@ def breakTransposition(message):
32 32 decryptedText = transpositionDecrypt.decryptMessage(key, message)
33 33
34 34 if detectEnglish.isEnglish(decryptedText):
35   - # Check with the user to see if the decrypted key has been found.
  35 + # Check with user to see if the decrypted key has been found.
36 36 print()
37 37 print('Possible encryption break:')
38 38 print('Key ' + str(key) + ': ' + decryptedText[:100])
6 transpositionDecrypt.py
@@ -28,11 +28,11 @@ def decryptMessage(key, message):
28 28 # The number of "shaded boxes" in the last "column" of the grid:
29 29 numOfShadedBoxes = (numOfColumns * numOfRows) - len(message)
30 30
31   - # Each string in plaintext represents a column in the transposition grid.
  31 + # Each string in plaintext represents a column in the grid.
32 32 plaintext = [''] * numOfColumns
33 33
34   - # The col and row variables point to where in the grid the next character
35   - # in the encrypted message will go.
  34 + # The col and row variables point to where in the grid the next
  35 + # character in the encrypted message will go.
36 36 col = 0
37 37 row = 0
38 38
6 transpositionEncrypt.py
@@ -10,8 +10,8 @@ def main():
10 10 translated = encryptMessage(myKey, myMessage)
11 11
12 12 # Print the (encrypted) string in translated to the screen, with
13   - # a | (called "pipe" character) after it in case there are spaces at the
14   - # end of the encrypted message.
  13 + # a | (called "pipe" character) after it in case there are spaces at
  14 + # the end of the encrypted message.
15 15 print(translated + '|')
16 16
17 17 # Copy the (encrypted) string in translated to the clipboard.
@@ -19,7 +19,7 @@ def main():
19 19
20 20
21 21 def encryptMessage(key, message):
22   - # Each string in ciphertext represents a column in the transposition grid.
  22 + # Each string in ciphertext represents a column in the grid.
23 23 ciphertext = [''] * key
24 24
25 25 # Loop through each column in ciphertext.
10 transpositionFileCipher.py
@@ -5,13 +5,13 @@
5 5
6 6 def main():
7 7 inputFilename = 'frankenstein.txt'
8   - # BE CAREFUL! If a file with the outputFilename name already exists, this
9   - # program will overwrite that file.
  8 + # BE CAREFUL! If a file with the outputFilename name already exists,
  9 + # this program will overwrite that file.
10 10 outputFilename = 'frankenstein.encrypted.txt'
11 11 myKey = 10
12 12 myMode = 'encrypt' # set to 'encrypt' or 'decrypt'
13 13
14   - # If the input file does not exist, then the program terminates early on.
  14 + # If the input file does not exist, then the program terminates early.
15 15 if not os.path.exists(inputFilename):
16 16 print('The file %s does not exist. Quitting...' % (inputFilename))
17 17 sys.exit()
@@ -48,7 +48,7 @@ def main():
48 48 print('%sed file is %s.' % (myMode.title(), outputFilename))
49 49
50 50
51   -# If transpositionCipherFile.py is run (instead of imported as a module) call
52   -# the main() function.
  51 +# If transpositionCipherFile.py is run (instead of imported as a module)
  52 +# call the main() function.
53 53 if __name__ == '__main__':
54 54 main()
78 vigenereBreaker.py
@@ -14,7 +14,7 @@
14 14
15 15 def main():
16 16 # Instead of typing this ciphertext out, you can copy & paste it
17   - # from http://inventwithpython.com/vigenereBreaker.py
  17 + # from http://invpy.com/vigenereBreaker.py
18 18 ciphertext = """ADIZ AVTZQECI TMZUBB WSA M PMILQEV HALPQAVTAKUOI, LGOUQDAF, KDMKTSVMZTSL, IZR XOEXGHZR KKUSITAAF. VZ WSA TWBHDG UBALMMZHDAD QZ HCE VMHSGOHUQBO OX KAAKULMD GXIWVOS, KRGDURDNY I RCMMSTUGVTAWZ CA TZM OCICWXFG JF "STSCMILPY" OID "UWYDPTSBUCI" WABT HCE LCDWIG EIOVDNW. BGFDNY QE KDDWTK QJNKQPSMEV BA PZ TZM ROOHWZ AT XOEXGHZR KKUSICW IZR VRLQRWXIST UBOEDTUUZNUM. PIMIFO ICMLV EMF DI, LCDWIG OWDYZD XWD HCE YWHSMNEMZH XOVM MBY CQXTSM SUPACG (GUKE) OO BDMFQCLWG BOMK, TZUHVIF'A OCYETZQOFIFO OSITJM. RCM A LQYS CE OIE VZAV WR VPT 8, LPQ GZCLQAB MEKXABNITTQ TJR YMDAVN FIHOG CJGBHVNSTKGDS. ZM PSQIKMP O IUEJQF JF LMOVIIICQG AOJ JDSVKAVS UZREIZ QDPZMDG, DNUTGRDNY BTS HELPAR JF LPQ PJMTM, MB ZLWKFFJMWKTOIIUIX AVCZQZS OHSB OCPLV NUBY SWBFWIGK NAF OHW MZWBMS UMQCIFM. MTOEJ BTS RAJ PQ KJRCMP OO TZM ZOOIGVMZ KHQAUQVL DINCMALWDM, RHWZQ VZ CJMMHZD GVQ CA TZM RWMSL LQGDGFA RCM A KBAFZD-HZAUMAE KAAKULMD, HCE SKQ. WI 1948 TMZUBB JGQZSY MSF ZSRMSV'E QJMHCFWIG DINCMALWDM VT EIZQCEKBQF PNADQFNILG, IVZRW PQ ONSAAFSY IF BTS YENMXCKMWVF CA TZM YOICZMEHZR UWYDPTWZE OID TMOOHE AVFSMEKBQR DN EIFVZMSBUQVL TQAZJGQ. PQ KMOLM M DVPWZ AB OHW KTSHIUIX PVSAA AT HOJXTCBEFMEWN, AFL BFZDAKFSY OKKUZGALQZU XHWUUQVL JMMQOIGVE GPCZ IE HCE TMXCPSGD-LVVBGBUBNKQ ZQOXTAWZ, KCIUP ISME XQDGO OTAQFQEV QZ HCE 1960K. BGFDNY'A TCHOKMJIVLABK FZSMTFSY IF I OFDMAVMZ KRGAQQPTAWZ WI 1952, WZMZ VJMGAQLPAD IOHN WWZQ GOIDT UZGEYIX WI TZM GBDTWL WWIGVWY. VZ AUKQDOEV BDSVTEMZH RILP RSHADM TCMMGVQG (XHWUUQVL UIEHMALQAB) VS SV MZOEJVMHDVW BA DMIKWZ. HPRAVS RDEV QZ 1954, XPSL WHSM TOW ISZKK JQTJRW PUG 42ID TQDHCDSG, RFJM UGMBDDW XAWNOFQZU. VN AVCIZSL LQHZREQZSY TZIF VDS VMMHC WSA EIDCALQ; VDS EWFVZR SVP GJMW WFVZRK JQZDENMP VDS VMMHC WSA MQXIVMZHVL. GV 10 ESKTWUNSM 2009, FGTXCRIFO MB DNLMDBZT UIYDVIYV, NFDTAAT DMIEM YWIIKBQF BOJLAB WRGEZ AVDW IZ CAFAKUOG PMJXWX AHWXCBY GV NSCADN AT OHW JDWOIKP SCQEJVYSIT XWD "HCE SXBOGLAVS KVY ZM ION TJMMHZD." SA AT HAQ 2012 I BFDVSBQ AZMTMD'G WIDT ION BWNAFZ TZM TCPSW WR ZJRVA IVDCZ EAIGD YZMBO TMZUBB A KBMHPTGZK DVRVWZ WA EFIOHZD."""
19 19 brokenCiphertext = breakVigenere(ciphertext)
20 20
@@ -28,11 +28,11 @@ def main():
28 28
29 29
30 30 def findRepeatSequences(ciphertext):
31   - # Goes through the ciphertext and finds any 3 to 5 letter sequences that
32   - # are repeated. Returns a dict with the keys of the sequence and value
33   - # of a list of spacings (the number of letters between the repeats.)
  31 + # Goes through the ciphertext and finds any 3 to 5 letter sequences
  32 + # that are repeated. Returns a dict with the keys of the sequence and
  33 + # value of a list of spacings (number of letters between the repeats.)
34 34
35   - # Take out all of the non-letter characters from the ciphertext string.
  35 + # Take out all of the non-letter characters from the ciphertext.
36 36 letterList = [] # start with a blank list
37 37 for letter in ciphertext:
38 38 if letter.isalpha():
@@ -51,8 +51,8 @@ def findRepeatSequences(ciphertext):
51 51 if ciphertext[i:i + seqLen] == seq:
52 52 # Found a repeated sequence.
53 53 if seq not in seqSpacings:
54   - # First time a repeat was found, create a blank list
55   - # for it in seqSpacings.
  54 + # First time a repeat was found, create a blank
  55 + # list for it in seqSpacings.
56 56 seqSpacings[seq] = []
57 57
58 58 # Append the spacing distance between the repeated
@@ -65,7 +65,7 @@ def getFactors(num):
65 65 # Returns a list of factors of num.
66 66 # For example, getFactors(28) returns [2, 14, 4, 7]
67 67
68   - # If we've calculated the factors before, they will be in FACTOR_CACHE.
  68 + # If we've calculated the factors before, they'll be in FACTOR_CACHE.
69 69 # In that case, just return a copy of the list of factors.
70 70 if num in FACTOR_CACHE:
71 71 return copy.copy(FACTOR_CACHE[num])
@@ -86,7 +86,7 @@ def getFactors(num):
86 86
87 87 def getMostCommonFactors(seqFactors):
88 88 # First, get a count of many times a factor occurs in seqFactors
89   - factorCounts = {} # key is a factor, value is the number of times it occurs
  89 + factorCounts = {} # key is a factor, value is how often if occurs
90 90 for seq in seqFactors:
91 91 factorList = seqFactors[seq]
92 92 for factor in factorList:
@@ -94,23 +94,27 @@ def getMostCommonFactors(seqFactors):
94 94 factorCounts[factor] = 0
95 95 factorCounts[factor] += 1
96 96
97   - # Second, put the factor and its count into a tuple, and make a list of these tuples so we can sort them.
  97 + # Second, put the factor and its count into a tuple, and make a list
  98 + # of these tuples so we can sort them.
98 99 factorsByCount = []
99 100 for factor in factorCounts:
100   - if factor < MAX_KEY_LENGTH: # also, don't include factors larger than MAX_KEY_LENGTH
  101 + # exclude factors larger than MAX_KEY_LENGTH
  102 + if factor < MAX_KEY_LENGTH:
101 103 factorsByCount.append( (factor, factorCounts[factor]) )
102   - factorsByCount.sort(key=lambda x: x[1], reverse=True) # sort the list by the factor count
103 104
104   - # Third, go through the factorsByCount list and cut off the list after you find a factor that is not within 50% of the size of the previous factor count.
  105 + # sort the list by the factor count
  106 + factorsByCount.sort(key=lambda x: x[1], reverse=True)
  107 +
  108 + # Third, go through the factorsByCount list and cut off the list
  109 + # after you find a factor that is not within 50% of the size of the
  110 + # previous factor count.
105 111 markCount = factorsByCount[0][1]
106 112 for i in range(1, len(factorsByCount)):
107 113 if markCount * 0.5 > factorsByCount[i][1]:
108   - factorsByCount = factorsByCount[:i] # set factorsByCount to the factors in the list up to i (and cutting off the rest)
  114 + # set factorsByCount to thelist up to i (and cut the rest)
  115 + factorsByCount = factorsByCount[:i]
109 116 break
110 117
111   - # The factors are more likely to be the true key length, so sort them by factor:
112   - #factorsByCount.sort(key=lambda x: x[0])
113   -
114 118 return factorsByCount
115 119
116 120
@@ -121,7 +125,7 @@ def getNthLetter(nth, keyLength, message):
121 125 # getNthLetter(3, 3, 'ABCABCABC') returns 'CCC'
122 126 # getNthLetter(1, 5, 'ABCABCABC') returns 'AC'
123 127
124   - # Use a "regular expression" to get rid of non-letters from the message.
  128 + # Use a "regular expression" remove non-letters from the message.
125 129 message = nonLettersPattern.sub('', message)
126 130
127 131 i = nth - 1
@@ -133,7 +137,8 @@ def getNthLetter(nth, keyLength, message):
133 137
134 138
135 139 def breakVigenere(ciphertext):
136   - # First, we need to do Kasiski Examination to figure out what the length of the ciphertext's encryption key is.
  140 + # First, we need to do Kasiski Examination to figure out what the
  141 + # length of the ciphertext's encryption key is.
137 142 if not SILENT_MODE:
138 143 print('Determining most likely key lengths with Kasiski Examination...')
139 144
@@ -146,17 +151,19 @@ def breakVigenere(ciphertext):
146 151 print()
147 152
148 153 for keyLength in allLikelyKeyLengths:
149   - #print('Attempting break with key length %s (%s possible keys)...' % (keyLength, NUM_MOST_FREQ_LETTERS ** keyLength))
  154 + print('Attempting break with key length %s (%s possible keys)...' % (keyLength, NUM_MOST_FREQ_LETTERS ** keyLength))
150 155 brokenCiphertext = attemptBreakWithKeyLength(ciphertext, keyLength)
151 156 if brokenCiphertext != None:
152 157 break
153 158
154   - # If none of the key lengths we found using Kasiski Examination worked, start brute forcing through key lengths.
  159 + # If none of the key lengths we found using Kasiski Examination
  160 + # worked, start brute forcing through key lengths.
155 161 if brokenCiphertext == None:
156 162 if not SILENT_MODE:
157 163 print('Unable to break message with likely key length(s). Brute forcing key length...')
158 164 for keyLength in range(1, MAX_KEY_LENGTH + 1):
159   - if keyLength not in allLikelyKeyLengths: # don't re-check key lengths we've already tried from Kasiski Examination
  165 + # don't re-check key lengths already tried from Kasiski
  166 + if keyLength not in allLikelyKeyLengths:
160 167 if not SILENT_MODE:
161 168 print('Attempting break with key length %s (%s possible keys)...' % (keyLength, NUM_MOST_FREQ_LETTERS ** keyLength))
162 169 brokenCiphertext = attemptBreakWithKeyLength(ciphertext, keyLength)
@@ -167,12 +174,14 @@ def breakVigenere(ciphertext):
167 174
168 175
169 176 def kasiskiExamination(ciphertext):
170   - # Find out the sequences of 3 to 5 letters that occurr multiple times in the ciphertext.
171   - # repeatedSeqs has a value like: {'EXG': [192], 'NAF': [339, 972, 633], ... }
  177 + # Find out the sequences of 3 to 5 letters that occurr multiple times
  178 + # in the ciphertext. repeatedSeqs has a value like:
  179 + # {'EXG': [192], 'NAF': [339, 972, 633], ... }
172 180 repeatedSeqs = findRepeatSequences(ciphertext)
173 181
174   - # seqFactors keys are sequences, values are list of factors of the spacings.
175   - # seqFactos has a value like: {'GFD': [2, 3, 4, 6, 9, 12, 18, 23, 36, 46, 69, 92, 138, 207, 276, 414], 'ALW': [2, 3, 4, 6, ...], ...}
  182 + # seqFactors keys are sequences, values are list of factors of the
  183 + # spacings. seqFactos has a value like: {'GFD': [2, 3, 4, 6, 9, 12,
  184 + # 18, 23, 36, 46, 69, 92, 138, 207], 'ALW': [2, 3, 4, 6, ...], ...}
176 185 seqFactors = {}
177 186 for seq in repeatedSeqs:
178 187 seqFactors[seq] = []
@@ -180,10 +189,12 @@ def kasiskiExamination(ciphertext):
180 189 seqFactors[seq].extend(getFactors(spacing))
181 190
182 191 # factorsByCount is a list of tuples: (factor, factorCount)
183   - # factorsByCount has a value like: [(3, 497), (2, 487), (6, 453), (4, 284), (12, 260)]
  192 + # factorsByCount has a value like: [(3, 497), (2, 487), (6, 453), ...]
184 193 factorsByCount = getMostCommonFactors(seqFactors)
185 194
186   - # Now we extract the factor counts from factorsByCount and put them in variables named allLikelyKeyLengths and allLikelyKeyLengthsStr so that they are easier to use later.
  195 + # Now we extract the factor counts from factorsByCount and put them
  196 + # in variables named allLikelyKeyLengths and allLikelyKeyLengthsStr
  197 + # so that they are easier to use later.
187 198 allLikelyKeyLengths = []
188 199 for i in range(len(factorsByCount)):
189 200 allLikelyKeyLengths.append(factorsByCount[i][0])
@@ -200,7 +211,8 @@ def attemptBreakWithKeyLength(ciphertext, mostLikelyKeyLength):
200 211 for nth in range(1, mostLikelyKeyLength + 1):
201 212 nthLetters = getNthLetter(nth, mostLikelyKeyLength, ciphertext)
202 213
203   - # freqScores is a list of tuples (<letter>, <eng. freq. match score>)
  214 + # freqScores is a list of tuples like:
  215 + # [(<letter>, <Eng. Freq. match score>), ... ]
204 216 # This list is sorted by match score (a lower score means a better
205 217 # match. See the englishFreqMatch() comments in freqFinder).
206 218 freqScores = []
@@ -208,16 +220,14 @@ def attemptBreakWithKeyLength(ciphertext, mostLikelyKeyLength):
208 220 translated = vigenereCipher.decryptMessage(possibleKey, nthLetters)
209 221 freqScores.append((possibleKey, freqFinder.englishFreqMatch(translated)))
210 222
211   - # Each value in freqScores is a tuple (<letter>, <match score>). Since
212   - # we want to sort by match score, we need to pass a "lambda" function
213   - # to sort()'s "key" parameter to look at the value at the [1] index.
  223 + # Sort by match score
214 224 freqScores.sort(key=lambda x: x[1], reverse=True)
215 225
216 226 allFreqScores.append(freqScores[:NUM_MOST_FREQ_LETTERS])
217 227
218 228 if not SILENT_MODE:
219 229 for i in range(len(allFreqScores)):
220   - # use i+1, because otherwise the "first" letter is called the "0th" letter
  230 + # use i+1 so the first letter is not called the "0th" letter
221 231 print('Possible letters for letter %s of the key: ' % (i + 1), end='')
222 232 for freqScore in allFreqScores[i]:
223 233 print('%s ' % freqScore[0], end='')
@@ -238,7 +248,7 @@ def attemptBreakWithKeyLength(ciphertext, mostLikelyKeyLength):
238 248
239 249 if freqFinder.englishTrigramMatch(decryptedText):
240 250 if detectEnglish.isEnglish(decryptedText):
241   - # Check with the user to see if the decrypted key has been found.
  251 + # Check with the user to see if the key has been found.
242 252 print()
243 253 print('Possible encryption break:')
244 254 print('Key ' + str(possibleKey) + ': ' + decryptedText[:200])
1  vigenereCipher.py
@@ -6,6 +6,7 @@
6 6 LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
7 7
8 8 def main():
  9 + # This text can be copy/pasted from http://invpy.com/vigenereCipher.py
9 10 message = """Alan Mathison Turing was a British mathematician, logician, cryptanalyst, and computer scientist. He was highly influential in the development of computer science, providing a formalisation of the concepts of "algorithm" and "computation" with the Turing machine. Turing is widely considered to be the father of computer science and artificial intelligence. During World War II, Turing worked for the Government Code and Cypher School (GCCS) at Bletchley Park, Britain's codebreaking centre. For a time he was head of Hut 8, the section responsible for German naval cryptanalysis. He devised a number of techniques for breaking German ciphers, including the method of the bombe, an electromechanical machine that could find settings for the Enigma machine. After the war he worked at the National Physical Laboratory, where he created one of the first designs for a stored-program computer, the ACE. In 1948 Turing joined Max Newman's Computing Laboratory at Manchester University, where he assisted in the development of the Manchester computers and became interested in mathematical biology. He wrote a paper on the chemical basis of morphogenesis, and predicted oscillating chemical reactions such as the Belousov-Zhabotinsky reaction, which were first observed in the 1960s. Turing's homosexuality resulted in a criminal prosecution in 1952, when homosexual acts were still illegal in the United Kingdom. He accepted treatment with female hormones (chemical castration) as an alternative to prison. Turing died in 1954, just over two weeks before his 42nd birthday, from cyanide poisoning. An inquest determined that his death was suicide; his mother and some others believed his death was accidental. On 10 September 2009, following an Internet campaign, British Prime Minister Gordon Brown made an official public apology on behalf of the British government for "the appalling way he was treated." As of May 2012 a private member's bill was before the House of Lords which would grant Turing a statutory pardon if enacted."""
10 11 key = 'ASIMOV'
11 12 mode = 'encrypt' # set to 'encrypt' or 'decrypt'

0 comments on commit 24fc561

Please sign in to comment.
Something went wrong with that request. Please try again.