Browse files

Changes from unit testing.

  • Loading branch information...
1 parent dd74989 commit 24fc561df014dc7eb8f7c6f4349c8722227cd7d3 @asweigart committed Sep 1, 2012
View
2 affineBreaker.py
@@ -7,7 +7,7 @@
def main():
# You might want to copy & paste this text from the source code at
- # http://inventwithpython.com/affineBreaker.py
+ # http://invpy.com/affineBreaker.py
myMessage = 'H RZPEDYBO NZDKW WBTBOIB YZ MB RHKKBW VUYBKKVLBUY VG VY RZDKW WBRBVIB H QDPHU VUYZ MBKVBIVUL YQHY VY NHT QDPHU. -HKHU YDOVUL'
brokenCiphertext = breakAffine(myMessage.upper())
View
4 caesarBreaker.py
@@ -13,7 +13,7 @@
# The rest of the program is the same as the original Caesar program:
- # run the encryption/decryption code on each symbol in the message string
+ # run the encryption/decryption code on each symbol in the message
for symbol in message:
if symbol in LETTERS:
num = LETTERS.find(symbol) # get the number of the symbol
@@ -23,7 +23,7 @@
if num < 0:
num = num + len(LETTERS)
- # add encrypted/decrypted number's symbol at the end of translated
+ # add number's symbol at the end of translated
translated = translated + LETTERS[num]
else:
View
6 caesarCipher.py
@@ -13,7 +13,7 @@
mode = 'encrypt' # set to 'encrypt' or 'decrypt'
# every possible symbol that can be encrypted
-LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+LETTERS = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'
# stores the encrypted/decrypted form of the message
translated = ''
@@ -31,8 +31,8 @@
elif mode == 'decrypt':
num = num - key
- # handle the wrap around if num is larger than the length of LETTERS
- # or less than 0
+ # handle the wrap around if num is larger than the length of
+ # LETTERS or less than 0
if num >= len(LETTERS):
num = num - len(LETTERS)
elif num < 0:
View
29 codebreaker_unit_tests.py
@@ -296,13 +296,10 @@ def test_transpositionTestProgram(self):
self.assertTrue('Transposition cipher test passed.' in procOut)
- def test_detectEnglishProgram(self):
- proc = subprocess.Popen('c:\\python32\\python.exe detectEnglish.py', stdout=subprocess.PIPE, stdin=subprocess.PIPE)
- procOut = proc.communicate()[0].decode('ascii')
-
- 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'
+ def test_detectEnglishModule(self):
+ import detectEnglish
- self.assertEqual(procOut, expectedOutput)
+ self.assertTrue(detectEnglish.isEnglish(FOX_MESSAGE))
def test_affineCipherProgram(self):
@@ -441,28 +438,12 @@ def test_vigenereBreakerProgram(self):
procOut = proc.communicate('D\n'.encode('ascii'))[0].decode('ascii')
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."""
- 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'
+ 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'
self.assertEqual(pyperclip.paste().decode('ascii'), expectedClipboard)
self.assertEqual(procOut, expectedOutput)
- def test_freqFinderProgram(self):
- proc = subprocess.Popen('c:\\python32\\python.exe freqFinder.py', stdout=subprocess.PIPE, stdin=subprocess.PIPE)
- procOut = proc.communicate()[0].decode('ascii')
-
- 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'
-
- self.assertEqual(procOut, expectedOutput)
-
-
- def test_primeSieveProgram(self):
- proc = subprocess.Popen('c:\\python32\\python.exe primeSieve.py', stdout=subprocess.PIPE, stdin=subprocess.PIPE)
- procOut = proc.communicate()[0].decode('ascii')
-
- 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'
- self.assertEqual(procOut, expectedOutput)
-
def test_primeSieveModule(self):
import primeSieve
@@ -608,7 +589,7 @@ def test_vigenereCipherProgram(self):
if not TEST_ALL:
customSuite = unittest.TestSuite()
- customSuite.addTest(CodeBreakerUnitTests('test_primeSieveModule'))
+ customSuite.addTest(CodeBreakerUnitTests('test_vigenereBreakerProgram'))
unittest.TextTestRunner().run(customSuite)
elif TEST_ALL:
unittest.main()
View
25 detectEnglish.py
@@ -24,17 +24,6 @@ def loadDictionary(dictionaryFilename):
ENGLISH_WORDS = loadDictionary('dictionary.txt')
-def main():
- print('Testing the English detection module...')
- messages = ['The quick brown fox jumped over the yellow lazy dog.',
- 'Hello there. lkjjfldsf dsafk alf ewfewlfjl efa',
- 'Sumimasen. Kore wa nan desu ka?',
- '1100010110010111001011110000']
- for m in messages:
- print('%s\n\t%s\n' % (m, isEnglish(m)))
-
-
-# The getEnglishCount() function's code was copy/pasted from transpositionBreaker.py
def getEnglishCount(message):
message = message.upper()
message = re.sub('[^A-Z\s]', '', message)
@@ -51,9 +40,9 @@ def getEnglishCount(message):
def isEnglish(message, wordPercentage=20, letterPercentage=67):
- # By default, 20% of the words must be recognized as English words that
- # exist in the dictionary file, and 67% of all the characters in the
- # message must be letters (not punctuation, spaces, or numbers).
+ # By default, 20% of the words must exist in the dictionary file, and
+ # 67% of all the characters in the message must be letters (not
+ # punctuation, spaces, or numbers).
wordPercentage /= 100
letterPercentage /= 100
@@ -66,10 +55,4 @@ def isEnglish(message, wordPercentage=20, letterPercentage=67):
if symbol in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
numLetters += 1
- return (englishWords >= wordPercentage) and (numLetters / len(message) >= letterPercentage)
-
-
-# If detectEnglish.py is run (instead of imported as a module) call
-# the main() function.
-if __name__ == '__main__':
- main()
+ return (englishWords >= wordPercentage) and (numLetters / len(message) >= letterPercentage)
View
100 freqFinder.py
@@ -3,78 +3,14 @@
# frequency taken from http://en.wikipedia.org/wiki/Letter_frequency
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}
-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}
+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}
englishFreqOrder = tuple('ETAOINSHRDLCUMWFGYPBVKJXQZ')
ETAOIN = ''.join(englishFreqOrder)
LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
TRIGRAM_THRESHOLD = 2
TRIGRAM_MATCH_RANGE = 30
-def main():
- import random
-
- sonnet = """When, in disgrace with Fortune and men's eyes,
-I all alone beweep my outcast state,
-And trouble deaf heaven with my bootless cries,
-And look upon myself and curse my fate,
-Wishing me like to one more rich in hope,
-Featured like him, like him with friends possessed,
-Desiring this man's art, and that man's scope,
-With what I most enjoy contented least,
-Yet in these thoughts myself almost despising,
-Haply I think on thee, and then my state,
-Like to the lark at break of day arising
-From sullen earth, sings hymns at heaven's gate
-
-For thy sweet love remembered such wealth brings,
-That then I scorn to change my state with kings.""".upper()
-
- 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()
-
- 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."""))
-
- print('Shakespeare\'s Sonnet #29')
- print(sonnet)
- print()
-
- print('Letter Frequencies of Sonnet #29:')
- print(getLetterCount(sonnet))
- print()
-
- print('Frequency score of Sonnet #29:')
- print(englishFreqMatch(sonnet))
- print()
-
- # Scrambling Sonnet #29
- scrambled = list(sonnet)
- random.shuffle(scrambled)
- scrambled = ''.join(scrambled)
-
- print('Frequency score of Scrambled Sonnet #29:')
- print(englishFreqMatch(scrambled))
- print()
-
- print('Frequency score of Lorem Ipsum text:')
- print(englishFreqMatch(loremIpsum))
- print()
-
- print('Frequency score of alphabet:')
- print(englishFreqMatch(LETTERS))
- print()
-
- print('Frequency score of alphabet x 100:')
- print(englishFreqMatch(LETTERS * 100))
- print()
-
- print('Frequency score of "AAAAAAAAAAAAAAAH":')
- print(englishFreqMatch("AAAAAAAAAAAAAAAH"))
- print()
-
- print('Frequency score of "VDIUFRFDSFEWAFDSAFLKHFDSALKFA":')
- print(englishFreqMatch("VDIUFRFDSFEWAFDSAFLKHFDSALKFA"))
- print()
-
def getLetterCount(message):
# Returns a dictionary with keys of single letters and values of the
@@ -109,27 +45,31 @@ def getFrequencyOrder(message):
# frequently occurring in the message parameter.
message = message.upper()
- # first, get a dictionary of each letter and its frequency count from the message
+ # first, get a dictionary of each letter and its frequency count
letterToFreq = getLetterCount(message)
- # second, make a dictionary of each frequency count to each letter(s) with that frequency
+ # second, make a dictionary of each frequency count to each letter(s)
+ # with that frequency
freqToLetter = {}
for letter in LETTERS:
- freqToLetter[letterToFreq[letter]] = [] # intialize to a blank list
+ freqToLetter[letterToFreq[letter]] = [] # start as a blank list
for letter in LETTERS:
freqToLetter[letterToFreq[letter]].append(letter)
- # third, put each list of letters in reverse "ETAOIN" order, and then convert it to a string
+ # third, put each list of letters in reverse "ETAOIN" order, and then
+ # convert it to a string
for freq in freqToLetter:
freqToLetter[freq].sort(key=ETAOIN.find, reverse=True)
freqToLetter[freq] = ''.join(freqToLetter[freq])
- # fourth, convert the freqToLetter dictionary to a list of tuple pairs (key, value), then sort them
+ # fourth, convert the freqToLetter dictionary to a list of tuple
+ # pairs (key, value), then sort them
freqPairs = list(freqToLetter.items())
freqPairs.sort(key=lambda x: x[0], reverse=True)
- # fifth, now that the letters are ordered by frequency, extract all the letters for the final string
+ # fifth, now that the letters are ordered by frequency, extract all
+ # the letters for the final string
freqOrder = ''
for freqPair in freqPairs:
freqOrder += freqPair[1]
@@ -138,11 +78,11 @@ def getFrequencyOrder(message):
def englishFreqMatch(message):
- # Return the number of matches that the string in the message parameter
- # has when its letter frequency is compared to English letter frequency.
- # A "match" is how many of its six most frequent and six least frequent
- # letters is among the six most frequent and six least frequent letters
- # for English.
+ # Return the number of matches that the string in the message
+ # parameter has when its letter frequency is compared to English
+ # letter frequency. A "match" is how many of its six most frequent
+ # and six least frequent letters is among the six most frequent and
+ # six least frequent letters for English.
freqOrder = getFrequencyOrder(message)
matches = 0
@@ -197,10 +137,4 @@ def englishTrigramMatch(message):
if commonTrig in topFreqLetters[:TRIGRAM_MATCH_RANGE]:
matches += 1
- return matches >= TRIGRAM_THRESHOLD
-
-
-# If freqFinder.py is run (instead of imported as a module) call
-# the main() function.
-if __name__ == '__main__':
- main()
+ return matches >= TRIGRAM_THRESHOLD
View
1 makeRsaKeys.py
@@ -83,7 +83,6 @@ def generateKey(keySize=DEFAULT_KEY_SIZE):
return (publicKey, privateKey)
-
# If makeRsaKeys.py is run (instead of imported as a module) call
# the main() function.
if __name__ == '__main__':
View
49 nullBreaker.py
@@ -3,41 +3,54 @@
import nullCipher, pyperclip, detectEnglish, itertools
-# There are two settings our breaking program needs to limit the range of the possible keys it checks.
-# 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.
-# 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'
-# 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.
+# There are two settings our breaking program needs to limit the range of
+# the possible keys it checks.
+# 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 0 through 9.
+# MAX_KEY_DIGITS is the largest amount of numbers in the key. A 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'
+# 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.
+
MAX_KEY_NUMBER = 9
-MAX_KEY_LENGTH = 5
+MAX_KEY_DIGITS = 5
SILENT_MODE = False
+# This can be copy/pasted from http://invpy.com/nullBreaker.py
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"""
-# 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.
+
def main():
- # 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.
+ # Calculate the number of keys that the current MAX_KEY_DIGITS and
+ # MAX_KEY_NUMBER values will cause the breaker program to go through.
possibleKeys = 0 # start the number of keys at 0.
- for i in range(1, MAX_KEY_LENGTH + 1):
- # 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.
- # 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.
+ for i in range(1, MAX_KEY_DIGITS + 1):
+ # To find the total number of possible keys, add the total number
+ # of keys for 1-digit keys, 2-digit keys, and so on up to
+ # MAX_KEY_DIGITS-digit keys.
+ # To find the number of keys with i digits in them, 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.
possibleKeys += MAX_KEY_NUMBER ** i
- # 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.
+ # After exiting the loop, the value in possibleKeys is the total number
+ # of keys for MAX_KEY_NUMBER and MAX_KEY_RANGE.
print('Max key number: %s' % MAX_KEY_NUMBER)
- print('Max key length: %s' % MAX_KEY_LENGTH)
+ print('Max key length: %s' % MAX_KEY_DIGITS)
print('Possible keys to try: %s' % (possibleKeys))
print()
- # Python programs can be stopped at any time by pressing Ctrl-C (on Windows) or Ctrl-D (on Mac and Linux)
+ # Python programs can be stopped at any time by pressing Ctrl-C (on
+ # Windows) or Ctrl-D (on Mac and Linux)
print('(Press Ctrl-C or Ctrl-D to quit at any time.)')
print('Breaking...')
- # The breakNull() function will have all the encryption breaking code in it, and return the original plaintext.
brokenMessage = breakNull(myMessage)
if brokenMessage != None:
- # The plaintext is displayed on the screen. For the convenience of the user, we copy the text of the code to the clipboard.
print('Copying broken message to clipboard:')
print(brokenMessage)
pyperclip.copy(brokenMessage)
@@ -46,9 +59,9 @@ def main():
def breakNull(ciphertext):
- # 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.
- # This is because the key '1 0' will decrypt differently than '1 0 0'.
- for keyLength in range(1, MAX_KEY_LENGTH + 1):
+ # The program needs to try keys of length 1 (such as '5'), of length 2
+ # (such as '5 3'), and so on up to length MAX_KEY_DIGITS.
+ for keyLength in range(1, MAX_KEY_DIGITS + 1):
for keyParts in itertools.product(range(MAX_KEY_NUMBER + 1), repeat=keyLength):
key = []
for digit in keyParts:
View
49 primeSieve.py
@@ -3,33 +3,11 @@
import math
-def main():
- print(' 2 is prime: %s' % (isPrime(2)))
- print(' 5 is prime: %s' % (isPrime(5)))
- print(' 11 is prime: %s' % (isPrime(11)))
- print(' 16 is prime: %s' % (isPrime(16)))
- print(' 17 is prime: %s' % (isPrime(17)))
- print('101 is prime: %s' % (isPrime(101)))
- print('126 is prime: %s' % (isPrime(126)))
- print('147 is prime: %s' % (isPrime(147)))
- print()
- primes = primeSieve(1000)
- print(' 2 is prime: %s' % (2 in primes))
- print(' 5 is prime: %s' % (5 in primes))
- print(' 11 is prime: %s' % (11 in primes))
- print(' 16 is prime: %s' % (16 in primes))
- print(' 17 is prime: %s' % (17 in primes))
- print('101 is prime: %s' % (101 in primes))
- print('126 is prime: %s' % (126 in primes))
- print('147 is prime: %s' % (147 in primes))
- print()
- _testPrimeFunctions()
-
def isPrime(num):
# Returns True if num is a prime number, otherwise False.
- # Note: Generally, isPrime() is slower than primeSieve()
+ # Note: Generally, isPrime() is slower than primeSieve().
# all numbers less than 2 are not prime
if num < 2:
@@ -63,27 +41,4 @@ def primeSieve(sieveSize):
if sieve[i] == True:
primes.append(i)
- return primes
-
-
-def _testPrimeFunctions():
- TEST_SIZE = 20000
- sievePrimes = primeSieve(TEST_SIZE)
-
- print('Testing if both functions are consistent with each other...')
- allCorrect = True
- for i in range(TEST_SIZE):
- if (i in sievePrimes and not isPrime(i)) or (i not in sievePrimes and isPrime(i)):
- print('The two functions disagree if %s is prime.' % (i))
- allCorrect = False
-
- if allCorrect:
- print('Test Passed: Both functions are consistent with each other.')
- else:
- print('ERROR! Both functions are not consistent with each other.')
-
-
-# If primeSieve.py is run (instead of imported as a module) call
-# the main() function.
-if __name__ == '__main__':
- main()
+ return primes
View
2 rabinMiller.py
@@ -20,7 +20,7 @@ def rabinMiller(num):
s = s // 2
t += 1
- for dummy_trials in range(5): # attempt to falsify num's primality 5 times
+ for dummy_trials in range(5): # try to falsify num's primality 5 times
a = random.randrange(2, num - 1)
v = pow(a, s, num)
if v != 1: # this test does not apply if v is 1.
View
15 rsaCipher.py
@@ -4,7 +4,8 @@
import sys
# IMPORTANT: The block size MUST be less or equal than the key size!
-# (Note: The block size is in bytes, the key size is in bits. There are 8 bits in 1 byte.)
+# (Note: The block size is in bytes, the key size is in bits. There
+# are 8 bits in 1 byte.)
DEFAULT_BLOCK_SIZE = 128
BYTE_SIZE = 256 # One byte has 256 different values.
@@ -48,17 +49,17 @@ def getBlocksFromText(message, blockSize=DEFAULT_BLOCK_SIZE):
def getTextFromBlocks(blockInts, messageLength, blockSize=DEFAULT_BLOCK_SIZE):
- # Converts a list of block integers to the original message string. The
- # original message length is needed to properly convert the last block
- # integer.
+ # Converts a list of block integers to the original message string.
+ # The original message length is needed to properly convert the last
+ # block integer.
message = []
for blockInt in blockInts:
blockMessage = []
for i in range(blockSize-1, -1, -1):
if len(message) + i < messageLength:
- # Decode the message string for the 512 (or whatever blockSize is
- # set to) characters from this block integer.
+ # Decode the message string for the 512 (or whatever
+ # blockSize is set to) characters from this block integer.
charNumber = blockInt // (BYTE_SIZE ** i)
blockInt = blockInt % (BYTE_SIZE ** i)
blockMessage.insert(0, bytes([charNumber]).decode('ascii'))
@@ -68,7 +69,7 @@ def getTextFromBlocks(blockInts, messageLength, blockSize=DEFAULT_BLOCK_SIZE):
def encryptMessage(message, key, blockSize=DEFAULT_BLOCK_SIZE):
# Converts the message string into a list of block integers, and then
- # encrypt each block integer. Be sure to pass the PUBLIC key to encrypt.
+ # encrypt each block integer. Pass the PUBLIC key to encrypt.
encryptedBlocks = []
n, e = key
View
82 simpleSubBreaker.py
@@ -3,28 +3,28 @@
"""
In this program, a "word pattern" is a description of which letters are
-repeated in a word. A word pattern is a series of numbers delimited by periods.
-The first letter to appear in the word is assigned 0, the second letter 1, and
-so on. So the word pattern for 'cucumber' is '0.1.0.1.2.3.4.5' because the
-first letter 'c' occurs as the first and third letter in the word 'cucumber'.
-So the pattern has '0' as the first and third number.
+repeated in a word. A word pattern is numbers delimited by periods.
+The first letter to appear in the word is assigned 0, the second letter 1,
+and so on. So the word pattern for 'cucumber' is '0.1.0.1.2.3.4.5' because the
+first letter 'c' occurs as the first and third letter in the word
+'cucumber'. So the pattern has '0' as the first and third number.
The pattern for 'abc' or 'cba' is '0.1.2'
The pattern for 'aaa' or 'bbb' is '0.0.0'
The pattern for 'hello' is '0.1.2.2.3'
-The pattern for 'advise' or 'closet' is '0.1.2.3.4.5' (they have only unique
-letters in the word)
+The pattern for 'advise' or 'closet' is '0.1.2.3.4.5' (they have only
+unique letters in the word)
In this program, a "candidate" is a possible English word that a ciphertext
work can decrypt to.
For example, 'cucumber', 'mementos', and 'cocoanut' are candidates for the
ciphertext word 'JHJHWDOV' (because all of them have the pattern
'0.1.0.1.2.3.4.5')
-In this program, a "map" or "mapping" is a dictionary where the keys are the
-letters in LETTERS (e.g. 'A', 'B', 'C', etc) and the values are lists of
-letters that could possibly be the correct decryption. If the list is blank,
-this means that it is unknown what this letter could decrypt to.
+In this program, a "map" or "mapping" is a dictionary where the keys are
+the letters in LETTERS (e.g. 'A', 'B', 'C', etc) and the values are lists
+of letters that could possibly be the correct decryption. If the list is
+blank, this means that it is unknown what this letter could decrypt to.
"""
# The import statement for wordPatterns is further down.
@@ -77,7 +77,7 @@ def main():
def getWordPattern(word):
# Returns a string of the pattern form of the given word.
- # e.g. '0.1.2.3.4.1.2.3.5.6' for 'DUSTBUSTER' or '0.1.2.2.3' for 'DOGGY'
+ # e.g. '0.1.2.3.4.1.2.3.5.6' for 'DUSTBUSTER'
word = word.upper()
nextNum = 0
letterNums = {}
@@ -129,14 +129,14 @@ def getNewBlankMapping():
def addMappings(theMap, cipherWord, candidate):
- # The theMap parameter is a "mapping" data structure that this function
- # modifies. (See the comments at the top of this file.)
+ # The theMap parameter is a "mapping" data structure that this
+ # function modifies. (See the comments at the top of this file.)
# The cipherWord parameter is a string value of the ciphertext word.
- # The candidate parameter is a possible English word that the cipherWord
- # could decrypt to.
+ # The candidate parameter is a possible English word that the
+ # cipherWord could decrypt to.
- # This function modifies theMap so that the mappings of the cipherWord's
- # letters to the candidate's letters are added to theMap.
+ # This function modifies theMap so that the mappings of the
+ # cipherWord's letters to the candidate's letters are added to theMap.
for i in range(len(cipherWord)):
if candidate[i] not in theMap[cipherWord[i]]:
@@ -150,8 +150,8 @@ def intersectMappings(mapA, mapB):
result = getNewBlankMapping()
for i in mapA.keys():
- # An empty list means "any letter is possible". So just copy the other
- # map entirely.
+ # An empty list means "any letter is possible". So just copy the
+ # other map entirely.
if mapA[i] == []:
result[i] = copy.copy(mapB[i])
elif mapB[i] == []:
@@ -165,14 +165,14 @@ def intersectMappings(mapA, mapB):
def removeSolvedLettersFromMapping(theMap):
- # Letters in the mapping that map to only one letter are consider "solved"
- # and can be removed from the other letters.
- # For example, if 'A' maps to possible letters ['M', 'N'], and 'B' maps
- # to ['N'], then we know that 'B' must map to 'N', so we can remove 'N'
- # from the list of what 'A' could map to. So 'A' then maps to ['M'].
- # Note that now that 'A' maps to only one letter, we can remove 'M'
- # from the list of possible mappings for every other letter. (This is why
- # there is a loop that keeps reducing the map.)
+ # Letters in the mapping that map to only one letter are consider
+ # "solved" and can be removed from the other letters.
+ # For example, if 'A' maps to possible letters ['M', 'N'], and 'B'
+ # maps to ['N'], then we know that 'B' must map to 'N', so we can
+ # remove 'N' from the list of what 'A' could map to. So 'A' then maps
+ # to ['M']. Note that now that 'A' maps to only one letter, we can
+ # remove 'M' from the list of possible mappings for every other
+ # letter. (This is why there is a loop that keeps reducing the map.)
previousSolvedLetters = []
solvedLetters = None
while previousSolvedLetters != solvedLetters:
@@ -181,24 +181,24 @@ def removeSolvedLettersFromMapping(theMap):
previousSolvedLetters = solvedLetters
solvedLetters = []
- # solvedLetters will be a list of English letters that have one and
- # only one possible mapping in theMap
+ # solvedLetters will be a list of English letters that have one
+ # and only one possible mapping in theMap
for i in theMap:
if len(theMap[i]) == 1:
solvedLetters.append(theMap[i][0])
# If a letter is solved, than it cannot possibly be a possible
- # decryption letter for a different ciphertext letter, so we should
- # remove it.
+ # decryption letter for a different ciphertext letter, so we
+ # should remove it.
for i in theMap:
for s in solvedLetters:
if len(theMap[i]) != 1 and s in theMap[i]:
theMap[i].remove(s)
- # With a letter removed, it's possible that we may have reduced other
- # ciphertext letters to one and only one solution, so keep looping
- # until previousSolvedLetters == solvedLetters. At that point, we'll
- # know we can't rmemove any more letters.
+ # With a letter removed, it's possible that we may have reduced
+ # other ciphertext letters to one and only one solution, so keep
+ # looping until previousSolvedLetters == solvedLetters. At that
+ # point, we'll know we can't rmemove any more letters.
return theMap
@@ -232,7 +232,7 @@ def decryptWithMap(ciphertext, theMap):
key = ['x'] * len(LETTERS)
for letter in theMap.keys():
if len(theMap[letter]) == 1:
- # There is only one possible letter mapping, so add it to the key.
+ # If only one possible letter mapping, add it to the key.
keyIndex = LETTERS.find(theMap[letter][0].upper())
key[keyIndex] = letter.upper()
else:
@@ -244,9 +244,9 @@ def decryptWithMap(ciphertext, theMap):
return simpleSubCipher.decryptMessage(key, ciphertext)
def checkForWordPatternsPy():
- # If the wordPatterns.py file does not exist, create it based on the words
- # in our dictionary text file, dictionary.txt.
- # (You can download this file from http://inventwithpython.com/dictionary.txt)
+ # If the wordPatterns.py file does not exist, create it based on the
+ # words in our dictionary text file, dictionary.txt.
+ # (Download this file from http://invpy.com/dictionary.txt)
if not os.path.exists('wordPatterns.py'):
import pprint # import the "pretty print" module
allPatterns = {}
@@ -256,7 +256,7 @@ def checkForWordPatternsPy():
fp.close()
for word in wordList:
- word = word.strip() # get rid of newline at the end of the string
+ word = word.strip() # get rid of newline at the end
pattern = getWordPattern(word)
if pattern not in allPatterns:
View
6 transpositionBreaker.py
@@ -5,7 +5,7 @@
def main():
# You might want to copy & paste this text from the source code at
- # http://inventwithpython.com/transpositionBreaker.py
+ # http://invpy.com/transpositionBreaker.py
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"""
brokenCiphertext = breakTransposition(myMessage)
@@ -14,7 +14,7 @@ def main():
print('Failed to break encryption.')
else:
print('Copying broken ciphertext to clipboard:')
- print(brokenCiphertext[:1000]) # only print the first 1000 characters
+ print(brokenCiphertext)
pyperclip.copy(brokenCiphertext)
@@ -32,7 +32,7 @@ def breakTransposition(message):
decryptedText = transpositionDecrypt.decryptMessage(key, message)
if detectEnglish.isEnglish(decryptedText):
- # Check with the user to see if the decrypted key has been found.
+ # Check with user to see if the decrypted key has been found.
print()
print('Possible encryption break:')
print('Key ' + str(key) + ': ' + decryptedText[:100])
View
6 transpositionDecrypt.py
@@ -28,11 +28,11 @@ def decryptMessage(key, message):
# The number of "shaded boxes" in the last "column" of the grid:
numOfShadedBoxes = (numOfColumns * numOfRows) - len(message)
- # Each string in plaintext represents a column in the transposition grid.
+ # Each string in plaintext represents a column in the grid.
plaintext = [''] * numOfColumns
- # The col and row variables point to where in the grid the next character
- # in the encrypted message will go.
+ # The col and row variables point to where in the grid the next
+ # character in the encrypted message will go.
col = 0
row = 0
View
6 transpositionEncrypt.py
@@ -10,16 +10,16 @@ def main():
translated = encryptMessage(myKey, myMessage)
# Print the (encrypted) string in translated to the screen, with
- # a | (called "pipe" character) after it in case there are spaces at the
- # end of the encrypted message.
+ # a | (called "pipe" character) after it in case there are spaces at
+ # the end of the encrypted message.
print(translated + '|')
# Copy the (encrypted) string in translated to the clipboard.
pyperclip.copy(translated)
def encryptMessage(key, message):
- # Each string in ciphertext represents a column in the transposition grid.
+ # Each string in ciphertext represents a column in the grid.
ciphertext = [''] * key
# Loop through each column in ciphertext.
View
10 transpositionFileCipher.py
@@ -5,13 +5,13 @@
def main():
inputFilename = 'frankenstein.txt'
- # BE CAREFUL! If a file with the outputFilename name already exists, this
- # program will overwrite that file.
+ # BE CAREFUL! If a file with the outputFilename name already exists,
+ # this program will overwrite that file.
outputFilename = 'frankenstein.encrypted.txt'
myKey = 10
myMode = 'encrypt' # set to 'encrypt' or 'decrypt'
- # If the input file does not exist, then the program terminates early on.
+ # If the input file does not exist, then the program terminates early.
if not os.path.exists(inputFilename):
print('The file %s does not exist. Quitting...' % (inputFilename))
sys.exit()
@@ -48,7 +48,7 @@ def main():
print('%sed file is %s.' % (myMode.title(), outputFilename))
-# If transpositionCipherFile.py is run (instead of imported as a module) call
-# the main() function.
+# If transpositionCipherFile.py is run (instead of imported as a module)
+# call the main() function.
if __name__ == '__main__':
main()
View
78 vigenereBreaker.py
@@ -14,7 +14,7 @@
def main():
# Instead of typing this ciphertext out, you can copy & paste it
- # from http://inventwithpython.com/vigenereBreaker.py
+ # from http://invpy.com/vigenereBreaker.py
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."""
brokenCiphertext = breakVigenere(ciphertext)
@@ -28,11 +28,11 @@ def main():
def findRepeatSequences(ciphertext):
- # Goes through the ciphertext and finds any 3 to 5 letter sequences that
- # are repeated. Returns a dict with the keys of the sequence and value
- # of a list of spacings (the number of letters between the repeats.)
+ # Goes through the ciphertext and finds any 3 to 5 letter sequences
+ # that are repeated. Returns a dict with the keys of the sequence and
+ # value of a list of spacings (number of letters between the repeats.)
- # Take out all of the non-letter characters from the ciphertext string.
+ # Take out all of the non-letter characters from the ciphertext.
letterList = [] # start with a blank list
for letter in ciphertext:
if letter.isalpha():
@@ -51,8 +51,8 @@ def findRepeatSequences(ciphertext):
if ciphertext[i:i + seqLen] == seq:
# Found a repeated sequence.
if seq not in seqSpacings:
- # First time a repeat was found, create a blank list
- # for it in seqSpacings.
+ # First time a repeat was found, create a blank
+ # list for it in seqSpacings.
seqSpacings[seq] = []
# Append the spacing distance between the repeated
@@ -65,7 +65,7 @@ def getFactors(num):
# Returns a list of factors of num.
# For example, getFactors(28) returns [2, 14, 4, 7]
- # If we've calculated the factors before, they will be in FACTOR_CACHE.
+ # If we've calculated the factors before, they'll be in FACTOR_CACHE.
# In that case, just return a copy of the list of factors.
if num in FACTOR_CACHE:
return copy.copy(FACTOR_CACHE[num])
@@ -86,31 +86,35 @@ def getFactors(num):
def getMostCommonFactors(seqFactors):
# First, get a count of many times a factor occurs in seqFactors
- factorCounts = {} # key is a factor, value is the number of times it occurs
+ factorCounts = {} # key is a factor, value is how often if occurs
for seq in seqFactors:
factorList = seqFactors[seq]
for factor in factorList:
if factor not in factorCounts:
factorCounts[factor] = 0
factorCounts[factor] += 1
- # Second, put the factor and its count into a tuple, and make a list of these tuples so we can sort them.
+ # Second, put the factor and its count into a tuple, and make a list
+ # of these tuples so we can sort them.
factorsByCount = []
for factor in factorCounts:
- if factor < MAX_KEY_LENGTH: # also, don't include factors larger than MAX_KEY_LENGTH
+ # exclude factors larger than MAX_KEY_LENGTH
+ if factor < MAX_KEY_LENGTH:
factorsByCount.append( (factor, factorCounts[factor]) )
- factorsByCount.sort(key=lambda x: x[1], reverse=True) # sort the list by the factor count
- # 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.
+ # sort the list by the factor count
+ factorsByCount.sort(key=lambda x: x[1], reverse=True)
+
+ # 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.
markCount = factorsByCount[0][1]
for i in range(1, len(factorsByCount)):
if markCount * 0.5 > factorsByCount[i][1]:
- factorsByCount = factorsByCount[:i] # set factorsByCount to the factors in the list up to i (and cutting off the rest)
+ # set factorsByCount to thelist up to i (and cut the rest)
+ factorsByCount = factorsByCount[:i]
break
- # The factors are more likely to be the true key length, so sort them by factor:
- #factorsByCount.sort(key=lambda x: x[0])
-
return factorsByCount
@@ -121,7 +125,7 @@ def getNthLetter(nth, keyLength, message):
# getNthLetter(3, 3, 'ABCABCABC') returns 'CCC'
# getNthLetter(1, 5, 'ABCABCABC') returns 'AC'
- # Use a "regular expression" to get rid of non-letters from the message.
+ # Use a "regular expression" remove non-letters from the message.
message = nonLettersPattern.sub('', message)
i = nth - 1
@@ -133,7 +137,8 @@ def getNthLetter(nth, keyLength, message):
def breakVigenere(ciphertext):
- # First, we need to do Kasiski Examination to figure out what the length of the ciphertext's encryption key is.
+ # First, we need to do Kasiski Examination to figure out what the
+ # length of the ciphertext's encryption key is.
if not SILENT_MODE:
print('Determining most likely key lengths with Kasiski Examination...')
@@ -146,17 +151,19 @@ def breakVigenere(ciphertext):
print()
for keyLength in allLikelyKeyLengths:
- #print('Attempting break with key length %s (%s possible keys)...' % (keyLength, NUM_MOST_FREQ_LETTERS ** keyLength))
+ print('Attempting break with key length %s (%s possible keys)...' % (keyLength, NUM_MOST_FREQ_LETTERS ** keyLength))
brokenCiphertext = attemptBreakWithKeyLength(ciphertext, keyLength)
if brokenCiphertext != None:
break
- # If none of the key lengths we found using Kasiski Examination worked, start brute forcing through key lengths.
+ # If none of the key lengths we found using Kasiski Examination
+ # worked, start brute forcing through key lengths.
if brokenCiphertext == None:
if not SILENT_MODE:
print('Unable to break message with likely key length(s). Brute forcing key length...')
for keyLength in range(1, MAX_KEY_LENGTH + 1):
- if keyLength not in allLikelyKeyLengths: # don't re-check key lengths we've already tried from Kasiski Examination
+ # don't re-check key lengths already tried from Kasiski
+ if keyLength not in allLikelyKeyLengths:
if not SILENT_MODE:
print('Attempting break with key length %s (%s possible keys)...' % (keyLength, NUM_MOST_FREQ_LETTERS ** keyLength))
brokenCiphertext = attemptBreakWithKeyLength(ciphertext, keyLength)
@@ -167,23 +174,27 @@ def breakVigenere(ciphertext):
def kasiskiExamination(ciphertext):
- # Find out the sequences of 3 to 5 letters that occurr multiple times in the ciphertext.
- # repeatedSeqs has a value like: {'EXG': [192], 'NAF': [339, 972, 633], ... }
+ # Find out the sequences of 3 to 5 letters that occurr multiple times
+ # in the ciphertext. repeatedSeqs has a value like:
+ # {'EXG': [192], 'NAF': [339, 972, 633], ... }
repeatedSeqs = findRepeatSequences(ciphertext)
- # seqFactors keys are sequences, values are list of factors of the spacings.
- # 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, ...], ...}
+ # seqFactors keys are sequences, values are list of factors of the
+ # spacings. seqFactos has a value like: {'GFD': [2, 3, 4, 6, 9, 12,
+ # 18, 23, 36, 46, 69, 92, 138, 207], 'ALW': [2, 3, 4, 6, ...], ...}
seqFactors = {}
for seq in repeatedSeqs:
seqFactors[seq] = []
for spacing in repeatedSeqs[seq]:
seqFactors[seq].extend(getFactors(spacing))
# factorsByCount is a list of tuples: (factor, factorCount)
- # factorsByCount has a value like: [(3, 497), (2, 487), (6, 453), (4, 284), (12, 260)]
+ # factorsByCount has a value like: [(3, 497), (2, 487), (6, 453), ...]
factorsByCount = getMostCommonFactors(seqFactors)
- # 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.
+ # 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.
allLikelyKeyLengths = []
for i in range(len(factorsByCount)):
allLikelyKeyLengths.append(factorsByCount[i][0])
@@ -200,24 +211,23 @@ def attemptBreakWithKeyLength(ciphertext, mostLikelyKeyLength):
for nth in range(1, mostLikelyKeyLength + 1):
nthLetters = getNthLetter(nth, mostLikelyKeyLength, ciphertext)
- # freqScores is a list of tuples (<letter>, <eng. freq. match score>)
+ # freqScores is a list of tuples like:
+ # [(<letter>, <Eng. Freq. match score>), ... ]
# This list is sorted by match score (a lower score means a better
# match. See the englishFreqMatch() comments in freqFinder).
freqScores = []
for possibleKey in LETTERS:
translated = vigenereCipher.decryptMessage(possibleKey, nthLetters)
freqScores.append((possibleKey, freqFinder.englishFreqMatch(translated)))
- # Each value in freqScores is a tuple (<letter>, <match score>). Since
- # we want to sort by match score, we need to pass a "lambda" function
- # to sort()'s "key" parameter to look at the value at the [1] index.
+ # Sort by match score
freqScores.sort(key=lambda x: x[1], reverse=True)
allFreqScores.append(freqScores[:NUM_MOST_FREQ_LETTERS])
if not SILENT_MODE:
for i in range(len(allFreqScores)):
- # use i+1, because otherwise the "first" letter is called the "0th" letter
+ # use i+1 so the first letter is not called the "0th" letter
print('Possible letters for letter %s of the key: ' % (i + 1), end='')
for freqScore in allFreqScores[i]:
print('%s ' % freqScore[0], end='')
@@ -238,7 +248,7 @@ def attemptBreakWithKeyLength(ciphertext, mostLikelyKeyLength):
if freqFinder.englishTrigramMatch(decryptedText):
if detectEnglish.isEnglish(decryptedText):
- # Check with the user to see if the decrypted key has been found.
+ # Check with the user to see if the key has been found.
print()
print('Possible encryption break:')
print('Key ' + str(possibleKey) + ': ' + decryptedText[:200])
View
1 vigenereCipher.py
@@ -6,6 +6,7 @@
LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
def main():
+ # This text can be copy/pasted from http://invpy.com/vigenereCipher.py
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."""
key = 'ASIMOV'
mode = 'encrypt' # set to 'encrypt' or 'decrypt'

0 comments on commit 24fc561

Please sign in to comment.