Browse files

Various changes. This completely breaks the unit tests, I know. I'll …

…update the unit tests later.
  • Loading branch information...
1 parent b36a8ab commit fb8aa2e35cc75ff110d5db438873b9113889481e @asweigart committed Jan 10, 2013
Showing with 1,087 additions and 1,161 deletions.
  1. +81 −81 affineCipher.py
  2. +59 −59 affineHacker.py
  3. +10 −10 affineKeyTest.py
  4. +125 −139 freqFinder.py
  5. +73 −88 makeRsaKeys.py
  6. +45 −44 primeSieve.py
  7. +163 −157 pyperclip.py
  8. +62 −70 rabinMiller.py
  9. +159 −159 rsaCipher.py
  10. +76 −76 simpleSubCipher.py
  11. +234 −278 simpleSubHacker.py
View
162 affineCipher.py
@@ -1,82 +1,82 @@
-# Affine Cipher
-# http://inventwithpython.com/hacking (BSD Licensed)
-
-import sys, pyperclip, cryptomath, random
-SYMBOLS = """ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"""
-
-
-def main():
- myMessage = """"A computer would deserve to be called intelligent if it could deceive a human into believing that it was human." -Alan Turing"""
- myKey = 2023
- myMode = 'encrypt' # set to 'encrypt' or 'decrypt'
-
- if myMode == 'encrypt':
- translated = encryptMessage(myKey, myMessage)
- elif myMode == 'decrypt':
- translated = decryptMessage(myKey, myMessage)
- print('Key: %s' % (myKey))
- print('%sed text:' % (myMode.title()))
- print(translated)
- pyperclip.copy(translated)
- print('Full %sed text copied to clipboard.' % (myMode))
-
-
-def getKeyParts(key):
- keyA = key // len(SYMBOLS)
- keyB = key - (keyA * len(SYMBOLS))
- return (keyA, keyB)
-
-
-def checkKeys(keyA, keyB, mode):
- if keyA == 1 and mode == 'encrypt':
- sys.exit('The affine cipher becomes incredibly weak when key A is set to 1. Choose a different key.')
- if keyB == 0 and mode == 'encrypt':
- sys.exit('The affine cipher becomes incredibly weak when key B is set to 0. Choose a different key.')
- if keyA < 0 or keyB < 0 or keyB > len(SYMBOLS) - 1:
- sys.exit('Key A must be greater than 0 and Key B must be between 0 and %s.' % (len(SYMBOLS) - 1))
- if cryptomath.gcd(keyA, len(SYMBOLS)) != 1:
- sys.exit('Key A (%s) and the symbol set size (%s) are not relatively prime. Choose a different key.' % (keyA, len(SYMBOLS)))
-
-
-def encryptMessage(key, message):
- keyA, keyB = getKeyParts(key)
- checkKeys(keyA, keyB, 'encrypt')
- ciphertext = ''
- for symbol in message:
- if symbol in SYMBOLS:
- # encrypt this symbol
- symIndex = SYMBOLS.find(symbol)
- ciphertext += SYMBOLS[(symIndex * keyA + keyB) % len(SYMBOLS)]
- else:
- ciphertext += symbol # just append this symbol unencrypted
- return ciphertext
-
-
-def decryptMessage(key, message):
- keyA, keyB = getKeyParts(key)
- checkKeys(keyA, keyB, 'decrypt')
- keyA, keyB = getKeyParts(key)
- modInverseOfKeyA = cryptomath.findModInverse(keyA, len(SYMBOLS))
- plaintext = ''
- for symbol in message:
- if symbol in SYMBOLS:
- # decrypt this symbol
- symIndex = SYMBOLS.find(symbol)
- plaintext += SYMBOLS[( (symIndex - keyB) % len(SYMBOLS)) * modInverseOfKeyA % len(SYMBOLS)]
- else:
- plaintext += symbol # just append this symbol undecrypted
- return plaintext
-
-
-def getRandomKey():
- while True:
- keyA = random.randint(2, len(SYMBOLS))
- keyB = random.randint(2, len(SYMBOLS))
- if cryptomath.gcd(keyA, len(SYMBOLS)) != 1:
- return keyA * len(SYMBOLS) + keyB
-
-
-# If affineCipher.py is run (instead of imported as a module) call
-# the main() function.
-if __name__ == '__main__':
+# Affine Cipher
+# http://inventwithpython.com/hacking (BSD Licensed)
+
+import sys, pyperclip, cryptomath, random
+SYMBOLS = """ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"""
+
+
+def main():
+ myMessage = """"A computer would deserve to be called intelligent if it could deceive a human into believing that it was human." -Alan Turing"""
+ myKey = 2023
+ myMode = 'encrypt' # set to 'encrypt' or 'decrypt'
+
+ if myMode == 'encrypt':
+ translated = encryptMessage(myKey, myMessage)
+ elif myMode == 'decrypt':
+ translated = decryptMessage(myKey, myMessage)
+ print('Key: %s' % (myKey))
+ print('%sed text:' % (myMode.title()))
+ print(translated)
+ pyperclip.copy(translated)
+ print('Full %sed text copied to clipboard.' % (myMode))
+
+
+def getKeyParts(key):
+ keyA = key // len(SYMBOLS)
+ keyB = key % len(SYMBOLS)
+ return (keyA, keyB)
+
+
+def checkKeys(keyA, keyB, mode):
+ if keyA == 1 and mode == 'encrypt':
+ sys.exit('The affine cipher becomes incredibly weak when key A is set to 1. Choose a different key.')
+ if keyB == 0 and mode == 'encrypt':
+ sys.exit('The affine cipher becomes incredibly weak when key B is set to 0. Choose a different key.')
+ if keyA < 0 or keyB < 0 or keyB > len(SYMBOLS) - 1:
+ sys.exit('Key A must be greater than 0 and Key B must be between 0 and %s.' % (len(SYMBOLS) - 1))
+ if cryptomath.gcd(keyA, len(SYMBOLS)) != 1:
+ sys.exit('Key A (%s) and the symbol set size (%s) are not relatively prime. Choose a different key.' % (keyA, len(SYMBOLS)))
+
+
+def encryptMessage(key, message):
+ keyA, keyB = getKeyParts(key)
+ checkKeys(keyA, keyB, 'encrypt')
+ ciphertext = ''
+ for symbol in message:
+ if symbol in SYMBOLS:
+ # encrypt this symbol
+ symIndex = SYMBOLS.find(symbol)
+ ciphertext += SYMBOLS[(symIndex * keyA + keyB) % len(SYMBOLS)]
+ else:
+ ciphertext += symbol # just append this symbol unencrypted
+ return ciphertext
+
+
+def decryptMessage(key, message):
+ keyA, keyB = getKeyParts(key)
+ checkKeys(keyA, keyB, 'decrypt')
+ plaintext = ''
+ modInverseOfKeyA = cryptomath.findModInverse(keyA, len(SYMBOLS))
+
+ for symbol in message:
+ if symbol in SYMBOLS:
+ # decrypt this symbol
+ symIndex = SYMBOLS.find(symbol)
+ plaintext += SYMBOLS[( (symIndex - keyB) % len(SYMBOLS)) * modInverseOfKeyA % len(SYMBOLS)]
+ else:
+ plaintext += symbol # just append this symbol undecrypted
+ return plaintext
+
+
+def getRandomKey():
+ while True:
+ keyA = random.randint(2, len(SYMBOLS))
+ keyB = random.randint(2, len(SYMBOLS))
+ if cryptomath.gcd(keyA, len(SYMBOLS)) != 1:
+ return keyA * len(SYMBOLS) + keyB
+
+
+# If affineCipher.py is run (instead of imported as a module) call
+# the main() function.
+if __name__ == '__main__':
main()
View
118 affineHacker.py
@@ -1,60 +1,60 @@
-# Affine Cipher Hacker
-# http://inventwithpython.com/hacking (BSD Licensed)
-
-import pyperclip, affineCipher, detectEnglish, cryptomath
-
-SILENT_MODE = False
-
-def main():
- # You might want to copy & paste this text from the source code at
- # http://invpy.com/affineHacker.py
- myMessage = """U&'<3dJ^Gjx'-3^MS'Sj0jxuj'G3'%j'<mMMjS'g{GjMMg9j{G'g"'gG'<3^MS'Sj<jguj'm'P^dm{'g{G3'%jMgjug{9'GPmG'gG'-m0'P^dm{LU'5&Mm{'_^xg{9"""
-
- hackedMessage = hackAffine(myMessage)
-
- if hackedMessage != 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 hacked message to clipboard:')
- print(hackedMessage)
- pyperclip.copy(hackedMessage)
- else:
- print('Failed to hack encryption.')
-
-
-def hackAffine(message):
- print('Hacking...')
-
- # 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.)')
-
- # brute force by looping through every possible key
- for key in range(len(affineCipher.SYMBOLS) ** 2):
- keyA = affineCipher.getKeyParts(key)[0]
- if cryptomath.gcd(keyA, len(affineCipher.SYMBOLS)) != 1:
- continue
-
- decryptedText = affineCipher.decryptMessage(key, message)
- if not SILENT_MODE:
- print('Tried Key %s... (%s)' % (key, decryptedText[:40]))
-
- if detectEnglish.isEnglish(decryptedText):
- # Check with the user if the decrypted key has been found.
- print()
- print('Possible encryption hack:')
- print('Key: %s' % (key))
- print('Decrypted message: ' + decryptedText[:200])
- print()
- print('Enter D for done, or just press Enter to continue hacking:')
- response = input('> ')
-
- if response.strip().upper().startswith('D'):
- return decryptedText
- return None
-
-
-# If affineHacker.py is run (instead of imported as a module) call
-# the main() function.
-if __name__ == '__main__':
+# Affine Cipher Hacker
+# http://inventwithpython.com/hacking (BSD Licensed)
+
+import pyperclip, affineCipher, detectEnglish, cryptomath
+
+SILENT_MODE = False
+
+def main():
+ # You might want to copy & paste this text from the source code at
+ # http://invpy.com/affineHacker.py
+ myMessage = """U&'<3dJ^Gjx'-3^MS'Sj0jxuj'G3'%j'<mMMjS'g{GjMMg9j{G'g"'gG'<3^MS'Sj<jguj'm'P^dm{'g{G3'%jMgjug{9'GPmG'gG'-m0'P^dm{LU'5&Mm{'_^xg{9"""
+
+ hackedMessage = hackAffine(myMessage)
+
+ if hackedMessage != 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 hacked message to clipboard:')
+ print(hackedMessage)
+ pyperclip.copy(hackedMessage)
+ else:
+ print('Failed to hack encryption.')
+
+
+def hackAffine(message):
+ print('Hacking...')
+
+ # 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.)')
+
+ # brute force by looping through every possible key
+ for key in range(len(affineCipher.SYMBOLS) ** 2):
+ keyA = affineCipher.getKeyParts(key)[0]
+ if cryptomath.gcd(keyA, len(affineCipher.SYMBOLS)) != 1:
+ continue
+
+ decryptedText = affineCipher.decryptMessage(key, message)
+ if not SILENT_MODE:
+ print('Tried Key %s... (%s)' % (key, decryptedText[:40]))
+
+ if detectEnglish.isEnglish(decryptedText):
+ # Check with the user if the decrypted key has been found.
+ print()
+ print('Possible encryption hack:')
+ print('Key: %s' % (key))
+ print('Decrypted message: ' + decryptedText[:200])
+ print()
+ print('Enter D for done, or just press Enter to continue hacking:')
+ response = input('> ')
+
+ if response.strip().upper().startswith('D'):
+ return decryptedText
+ return None
+
+
+# If affineHacker.py is run (instead of imported as a module) call
+# the main() function.
+if __name__ == '__main__':
main()
View
20 affineKeyTest.py
@@ -1,11 +1,11 @@
-# This program proves that the keyspace of the affine cipher is limited
-# to len(SYMBOLS) ^ 2.
-
-import affineCipher, cryptomath
-
-message = 'Make things as simple as possible, but not simpler.'
-for keyA in range(2, len(affineCipher.SYMBOLS) + 5):
- key = keyA * len(affineCipher.SYMBOLS) + 1
-
- if cryptomath.gcd(keyA, len(affineCipher.SYMBOLS)) == 1:
+# This program proves that the keyspace of the affine cipher is limited
+# to len(SYMBOLS) ^ 2.
+
+import affineCipher, cryptomath
+
+message = 'Make things as simple as possible, but not simpler.'
+for keyA in range(2, len(affineCipher.SYMBOLS) + 5):
+ key = keyA * len(affineCipher.SYMBOLS) + 1
+
+ if cryptomath.gcd(keyA, len(affineCipher.SYMBOLS)) == 1:
print(keyA, affineCipher.encryptMessage(key, message))
View
264 freqFinder.py
@@ -1,140 +1,126 @@
-# Frequency Finder
-# http://inventwithpython.com/hacking (BSD Licensed)
-
-# 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.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 getLetterCount(message):
- # Returns a dictionary with keys of single letters and values of the
- # count of how many times they appear in the message parameter.
- letterToCount = {}
- for letter in LETTERS:
- letterToCount[letter] = 0 # intialize each letter to 0
-
- for letter in message:
- if letter in LETTERS:
- letterToCount[letter] += 1
-
- return letterToCount
-
-
-def getLetterFreq(message):
- # Returns a dictionary with keys of single letters and values of the
- # percentage of their frequency in the message parameter.
- counts = getLetterCount(message)
- totalCount = 0
- for letter in counts:
- totalCount += counts[letter]
-
- letterToFreq = {}
- for letter in counts:
- letterToFreq[letter] = round(counts[letter] * 100 / totalCount, 2)
- return letterToFreq
-
-
-def getFrequencyOrder(message):
- # Returns a string of the alphabet letters arranged in order of most
- # frequently occurring in the message parameter.
- message = message.upper()
-
- # 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
- freqToLetter = {}
- for letter in LETTERS:
- 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
- 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
- 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
- freqOrder = ''
- for freqPair in freqPairs:
- freqOrder += freqPair[1]
-
- return freqOrder
-
-
-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.
- freqOrder = getFrequencyOrder(message)
-
- matches = 0
- # Find how many matches for the six most common letters there are.
- for commonLetter in ETAOIN[:6]:
- if commonLetter in freqOrder[:6]:
- matches += 1
- # Find how many matches for the six least common letters there are.
- for uncommonLetter in ETAOIN[-6:]:
- if uncommonLetter in freqOrder[-6:]:
- matches += 1
-
- return matches
-
-
-def englishTrigramMatch(message):
- # Return True if the string in the message parameter matches the
- # trigram frequency of English.
-
- # Remove the non-letter characters from message
- message = message.upper()
- lettersOnly = []
- for character in message:
- if character in LETTERS:
- lettersOnly.append(character)
- message = ''.join(lettersOnly)
-
- # Count the trigrams in message
- total = 0
- trigrams = {}
- for i in range(len(message) - 2):
- trigram = message[i:i+3]
- if trigram in trigrams:
- trigrams[trigram] += 1
- else:
- trigrams[trigram] = 1
- total += 1
-
- # Sort the trigrams by frequency
- topFreqs = list(trigrams.items())
- topFreqs.sort(key=lambda x: x[1], reverse=True)
- topFreqLetters = []
- for item in topFreqs:
- topFreqLetters.append(item[0])
-
- trigramFreqs = {}
- for trigram in trigrams:
- trigramFreqs[trigram] = trigrams[trigram] / total * 100
-
- matches = 0
- for commonTrig in englishTrigramFreq:
- if commonTrig in topFreqLetters[:TRIGRAM_MATCH_RANGE]:
- matches += 1
-
+# Frequency Finder
+# http://inventwithpython.com/hacking (BSD Licensed)
+
+# 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.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 getLetterCount(message):
+ # Returns a dictionary with keys of single letters and values of the
+ # count of how many times they appear in the message parameter.
+ letterToCount = {}
+ for letter in LETTERS:
+ letterToCount[letter] = 0 # intialize each letter to 0
+
+ for letter in message:
+ if letter in LETTERS:
+ letterToCount[letter] += 1
+
+ return letterToCount
+
+
+def getFrequencyOrder(message):
+ # Returns a string of the alphabet letters arranged in order of most
+ # frequently occurring in the message parameter.
+ message = message.upper()
+
+ # 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
+ freqToLetter = {}
+ for letter in LETTERS:
+ 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
+ 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
+ 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
+ freqOrder = ''
+ for freqPair in freqPairs:
+ freqOrder += freqPair[1]
+
+ return freqOrder
+
+
+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.
+ freqOrder = getFrequencyOrder(message)
+
+ matches = 0
+ # Find how many matches for the six most common letters there are.
+ for commonLetter in ETAOIN[:6]:
+ if commonLetter in freqOrder[:6]:
+ matches += 1
+ # Find how many matches for the six least common letters there are.
+ for uncommonLetter in ETAOIN[-6:]:
+ if uncommonLetter in freqOrder[-6:]:
+ matches += 1
+
+ return matches
+
+
+def englishTrigramMatch(message):
+ # Return True if the string in the message parameter matches the
+ # trigram frequency of English.
+
+ # Remove the non-letter characters from message
+ message = message.upper()
+ lettersOnly = []
+ for character in message:
+ if character in LETTERS:
+ lettersOnly.append(character)
+ message = ''.join(lettersOnly)
+
+ # Count the trigrams in message
+ total = 0
+ trigrams = {}
+ for i in range(len(message) - 2):
+ trigram = message[i:i+3]
+ if trigram in trigrams:
+ trigrams[trigram] += 1
+ else:
+ trigrams[trigram] = 1
+ total += 1
+
+ # Sort the trigrams by frequency
+ topFreqs = list(trigrams.items())
+ topFreqs.sort(key=lambda x: x[1], reverse=True)
+ topFreqLetters = []
+ for item in topFreqs:
+ topFreqLetters.append(item[0])
+
+ trigramFreqs = {}
+ for trigram in trigrams:
+ trigramFreqs[trigram] = trigrams[trigram] / total * 100
+
+ matches = 0
+ for commonTrig in englishTrigramFreq:
+ if commonTrig in topFreqLetters[:TRIGRAM_MATCH_RANGE]:
+ matches += 1
+
return matches >= TRIGRAM_THRESHOLD
View
161 makeRsaKeys.py
@@ -1,89 +1,74 @@
-# RSA Key Generator
-# http://inventwithpython.com/hacking (BSD Licensed)
-
-import random, sys, os, rabinMiller, cryptomath
-
-OVERWRITE_MODE = True # CAREFUL! If True, you may overwrite your old keys!
-SILENT_MODE = False
-DEFAULT_KEY_SIZE = 1024
-
-def main():
- # create a public/private keypair with 1024 bit keys
- print('Making key files...')
- makeKeyFiles('al_sweigart', 1024)
- print('Key files made.')
-
-
-def makeKeyFiles(name, keySize=DEFAULT_KEY_SIZE):
- # Creates two files 'x_pubkey.txt' and 'x_privkey.txt' (where x is the
- # name parameter) with the the n,e and d,e integers written in them,
- # delimited by a comma.
-
- # Our safety check will prevent us from overwriting our old key files:
- if not OVERWRITE_MODE and (os.path.exists('%s_pubkey.txt' % (name)) or os.path.exists('%s_privkey.txt' % (name))):
- sys.exit('WARNING: The file %s_pubkey.txt or %s_privkey.txt already exists! Use a different name or set OVERWRITE_MODE to True and re-run this program.' % (name, name))
-
- publicKey, privateKey = generateKey(keySize)
-
- if not SILENT_MODE:
- print()
- print('The public key is a %s and %s digit number.' % (len(str(publicKey[0])), len(str(publicKey[1]))))
- print()
- print('Writing public key to file %s_pubkey.txt...' % (name))
- fp = open('%s_pubkey.txt' % (name), 'w')
- fp.write('%s,%s,%s' % (keySize, publicKey[0], publicKey[1]))
- fp.close()
-
- if not SILENT_MODE:
- print()
- print('The private key is a %s and %s digit number.' % (len(str(publicKey[0])), len(str(publicKey[1]))))
- print()
- print('Writing private key to file %s_privkey.txt...' % (name))
- fp = open('%s_privkey.txt' % (name), 'w')
- fp.write('%s,%s,%s' % (keySize, privateKey[0], privateKey[1]))
- fp.close()
-
-
-def generateKey(keySize=DEFAULT_KEY_SIZE):
- # Creates a public/private key pair with keys that are keySize bits in
- # size. This function may take a while to run.
-
- # Step 1: Create two prime numbers, p and q.
- if not SILENT_MODE:
- print('Generating p prime...')
- p = rabinMiller.generateLargePrime(keySize)
- if not SILENT_MODE:
- print('Generating q prime...')
- q = rabinMiller.generateLargePrime(keySize)
-
- # Step 2: Create a number e.
- if not SILENT_MODE:
- print('Generating e that is relatively prime to (p-1)*(q-1)...')
- while True:
- # Come up with an e that is relatively prime to (p-1)*(q-1)
- e = random.randrange(2 ** (keySize - 1), 2 ** (keySize))
- if cryptomath.gcd(e, (p - 1) * (q - 1)) == 1:
- break
- n = p * q
-
- # Step 3: Get the mod inverse of e.
- if not SILENT_MODE:
- print('Calculating d that is mod inverse of e...')
- d = cryptomath.findModInverse(e, (p - 1) * (q - 1))
-
- publicKey = (n, e)
- privateKey = (n, d)
-
- if not SILENT_MODE:
- print('Public key:')
- print(publicKey)
- print('Private key:')
- print(privateKey)
-
- return (publicKey, privateKey)
-
-
-# If makeRsaKeys.py is run (instead of imported as a module) call
-# the main() function.
-if __name__ == '__main__':
+# RSA Key Generator
+# http://inventwithpython.com/hacking (BSD Licensed)
+
+import random, sys, os, rabinMiller, cryptomath
+
+
+def main():
+ # create a public/private keypair with 1024 bit keys
+ print('Making key files...')
+ makeKeyFiles('al_sweigart', 1024)
+
+
+def generateKey(keySize):
+ # Creates a public/private key pair with keys that are keySize bits in
+ # size. This function may take a while to run.
+
+ # Step 1: Create two prime numbers, p and q. Calculate n = p * q.
+ print('Generating p prime...')
+ p = rabinMiller.generateLargePrime(keySize)
+ print('Generating q prime...')
+ q = rabinMiller.generateLargePrime(keySize)
+ n = p * q
+
+ # Step 2: Create a number e that is relatively prime to (p-1)*(q-1).
+ print('Generating e that is relatively prime to (p-1)*(q-1)...')
+ while True:
+ # Keep trying random numbers for e until one is valid.
+ e = random.randrange(2 ** (keySize - 1), 2 ** (keySize))
+ if cryptomath.gcd(e, (p - 1) * (q - 1)) == 1:
+ break
+
+ # Step 3: Calculate d, the mod inverse of e.
+ print('Calculating d that is mod inverse of e...')
+ d = cryptomath.findModInverse(e, (p - 1) * (q - 1))
+
+ publicKey = (n, e)
+ privateKey = (n, d)
+
+ print('Public key:', publicKey)
+ print('Private key:', privateKey)
+
+ return (publicKey, privateKey)
+
+
+def makeKeyFiles(name, keySize):
+ # Creates two files 'x_pubkey.txt' and 'x_privkey.txt' (where x is the
+ # value in name) with the the n,e and d,e integers written in them,
+ # delimited by a comma.
+
+ # Our safety check will prevent us from overwriting our old key files:
+ if os.path.exists('%s_pubkey.txt' % (name)) or os.path.exists('%s_privkey.txt' % (name)):
+ sys.exit('WARNING: The file %s_pubkey.txt or %s_privkey.txt already exists! Use a different name or delete these files and re-run this program.' % (name, name))
+
+ publicKey, privateKey = generateKey(keySize)
+
+ print()
+ print('The public key is a %s and a %s digit number.' % (len(str(publicKey[0])), len(str(publicKey[1]))))
+ print('Writing public key to file %s_pubkey.txt...' % (name))
+ fp = open('%s_pubkey.txt' % (name), 'w')
+ fp.write('%s,%s,%s' % (keySize, publicKey[0], publicKey[1]))
+ fp.close()
+
+ print()
+ print('The private key is a %s and a %s digit number.' % (len(str(publicKey[0])), len(str(publicKey[1]))))
+ print('Writing private key to file %s_privkey.txt...' % (name))
+ fp = open('%s_privkey.txt' % (name), 'w')
+ fp.write('%s,%s,%s' % (keySize, privateKey[0], privateKey[1]))
+ fp.close()
+
+
+# If makeRsaKeys.py is run (instead of imported as a module) call
+# the main() function.
+if __name__ == '__main__':
main()
View
89 primeSieve.py
@@ -1,44 +1,45 @@
-# Prime Number Sieve
-# http://inventwithpython.com/hacking (BSD Licensed)
-
-import math
-
-
-def isPrime(num):
- # Returns True if num is a prime number, otherwise False.
-
- # Note: Generally, isPrime() is slower than primeSieve().
-
- # all numbers less than 2 are not prime
- if num < 2:
- return False
-
- # see if num is divisible by any number up to the square root of num
- for i in range(2, int(math.sqrt(num)) + 1):
- if num % i == 0:
- return False
- return True
-
-
-def primeSieve(sieveSize):
- # Returns a list of prime numbers calculated using
- # the Sieve of Eratosthenes algorithm.
-
- sieve = [True] * sieveSize
- sieve[0] = False # zero and one are not prime numbers
- sieve[1] = False
-
- # create the sieve
- for i in range(2, int(math.sqrt(sieveSize)) + 1):
- pointer = i * 2
- while pointer < sieveSize:
- sieve[pointer] = False
- pointer += i
-
- # compile the list of primes
- primes = []
- for i in range(sieveSize):
- if sieve[i] == True:
- primes.append(i)
-
- return primes
+# Prime Number Sieve
+# http://inventwithpython.com/hacking (BSD Licensed)
+
+import math
+
+
+def isPrime(num):
+ # Returns True if num is a prime number, otherwise False.
+
+ # Note: Generally, isPrime() is slower than primeSieve().
+
+ # all numbers less than 2 are not prime
+ if num < 2:
+ return False
+
+ # see if num is divisible by any number up to the square root of num
+ for i in range(2, int(math.sqrt(num)) + 1):
+ if num % i == 0:
+ return False
+ return True
+
+
+def primeSieve(sieveSize):
+ # Returns a list of prime numbers calculated using
+ # the Sieve of Eratosthenes algorithm.
+
+ sieve = [True] * sieveSize
+ sieve[0] = False # zero and one are not prime numbers
+ sieve[1] = False
+
+ # create the sieve
+ for i in range(2, int(math.sqrt(sieveSize)) + 1):
+ pointer = i * 2
+ while pointer < sieveSize:
+ sieve[pointer] = False
+ pointer += i
+
+ # compile the list of primes
+ primes = []
+ for i in range(sieveSize):
+ if sieve[i] == True:
+ primes.append(i)
+
+ return primes
+import pyperclip; pyperclip.copy(primeSieve(1000))
View
320 pyperclip.py
@@ -1,158 +1,164 @@
-# Pyperclip v1.4
-
-# A cross-platform clipboard module for Python. (only handles plain text for now)
-# By Al Sweigart al@coffeeghost.net
-
-# Usage:
-# import pyperclip
-# pyperclip.copy('The text to be copied to the clipboard.')
-# spam = pyperclip.paste()
-
-# On Mac, this module makes use of the pbcopy and pbpaste commands, which should come with the os.
-# On Linux, this module makes use of the xclip command, which should come with the os. Otherwise run "sudo apt-get install xclip"
-
-
-# Copyright (c) 2010, Albert Sweigart
-# All rights reserved.
-#
-# BSD-style license:
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-# * Neither the name of the pyperclip nor the
-# names of its contributors may be used to endorse or promote products
-# derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY Albert Sweigart "AS IS" AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL Albert Sweigart BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# Change Log:
-# 1.2 Use the platform module to help determine OS.
-# 1.3 Changed ctypes.windll.user32.OpenClipboard(None) to ctypes.windll.user32.OpenClipboard(0), after some people ran into some TypeError
-
-import platform, os
-
-def winGetClipboard():
- ctypes.windll.user32.OpenClipboard(0)
- pcontents = ctypes.windll.user32.GetClipboardData(1) # 1 is CF_TEXT
- data = ctypes.c_char_p(pcontents).value
- #ctypes.windll.kernel32.GlobalUnlock(pcontents)
- ctypes.windll.user32.CloseClipboard()
- return data
-
-def winSetClipboard(text):
- GMEM_DDESHARE = 0x2000
- ctypes.windll.user32.OpenClipboard(0)
- ctypes.windll.user32.EmptyClipboard()
- try:
- # works on Python 2 (bytes() only takes one argument)
- hCd = ctypes.windll.kernel32.GlobalAlloc(GMEM_DDESHARE, len(bytes(text))+1)
- except TypeError:
- # works on Python 3 (bytes() requires an encoding)
- hCd = ctypes.windll.kernel32.GlobalAlloc(GMEM_DDESHARE, len(bytes(text, 'ascii'))+1)
- pchData = ctypes.windll.kernel32.GlobalLock(hCd)
- try:
- # works on Python 2 (bytes() only takes one argument)
- ctypes.cdll.msvcrt.strcpy(ctypes.c_char_p(pchData), bytes(text))
- except TypeError:
- # works on Python 3 (bytes() requires an encoding)
- ctypes.cdll.msvcrt.strcpy(ctypes.c_char_p(pchData), bytes(text, 'ascii'))
- ctypes.windll.kernel32.GlobalUnlock(hCd)
- ctypes.windll.user32.SetClipboardData(1, hCd)
- ctypes.windll.user32.CloseClipboard()
-
-def macSetClipboard(text):
- outf = os.popen('pbcopy', 'w')
- outf.write(text)
- outf.close()
-
-def macGetClipboard():
- outf = os.popen('pbpaste', 'r')
- content = outf.read()
- outf.close()
- return content
-
-def gtkGetClipboard():
- return gtk.Clipboard().wait_for_text()
-
-def gtkSetClipboard(text):
- global cb
- cb = gtk.Clipboard()
- cb.set_text(text)
- cb.store()
-
-def qtGetClipboard():
- return str(cb.text())
-
-def qtSetClipboard(text):
- cb.setText(text)
-
-def xclipSetClipboard(text):
- outf = os.popen('xclip -selection c', 'w')
- outf.write(text)
- outf.close()
-
-def xclipGetClipboard():
- outf = os.popen('xclip -selection c -o', 'r')
- content = outf.read()
- outf.close()
- return content
-
-def xselSetClipboard(text):
- outf = os.popen('xsel -i', 'w')
- outf.write(text)
- outf.close()
-
-def xselGetClipboard():
- outf = os.popen('xsel -o', 'r')
- content = outf.read()
- outf.close()
- return content
-
-
-if os.name == 'nt' or platform.system() == 'Windows':
- import ctypes
- getcb = winGetClipboard
- setcb = winSetClipboard
-elif os.name == 'mac' or platform.system() == 'Darwin':
- getcb = macGetClipboard
- setcb = macSetClipboard
-elif os.name == 'posix' or platform.system() == 'Linux':
- xclipExists = os.system('which xclip') == 0
- if xclipExists:
- getcb = xclipGetClipboard
- setcb = xclipSetClipboard
- else:
- xselExists = os.system('which xsel') == 0
- if xselExists:
- getcb = xselGetClipboard
- setcb = xselSetClipboard
- try:
- import gtk
- getcb = gtkGetClipboard
- setcb = gtkSetClipboard
- except Exception:
- try:
- import PyQt4.QtCore
- import PyQt4.QtGui
- app = PyQt4.QApplication([])
- cb = PyQt4.QtGui.QApplication.clipboard()
- getcb = qtGetClipboard
- setcb = qtSetClipboard
- except:
- raise Exception('Pyperclip requires the gtk or PyQt4 module installed, or the xclip command.')
-copy = setcb
+# Pyperclip v1.4
+
+# A cross-platform clipboard module for Python. (only handles plain text for now)
+# By Al Sweigart al@coffeeghost.net
+
+# Usage:
+# import pyperclip
+# pyperclip.copy('The text to be copied to the clipboard.')
+# spam = pyperclip.paste()
+
+# On Mac, this module makes use of the pbcopy and pbpaste commands, which should come with the os.
+# On Linux, this module makes use of the xclip command, which should come with the os. Otherwise run "sudo apt-get install xclip"
+
+
+# Copyright (c) 2010, Albert Sweigart
+# All rights reserved.
+#
+# BSD-style license:
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the pyperclip nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY Albert Sweigart "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL Albert Sweigart BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Change Log:
+# 1.2 Use the platform module to help determine OS.
+# 1.3 Changed ctypes.windll.user32.OpenClipboard(None) to ctypes.windll.user32.OpenClipboard(0), after some people ran into some TypeError
+
+import platform, os
+
+def winGetClipboard():
+ ctypes.windll.user32.OpenClipboard(0)
+ pcontents = ctypes.windll.user32.GetClipboardData(1) # 1 is CF_TEXT
+ data = ctypes.c_char_p(pcontents).value
+ #ctypes.windll.kernel32.GlobalUnlock(pcontents)
+ ctypes.windll.user32.CloseClipboard()
+ return data
+
+def winSetClipboard(text):
+ text = str(text)
+ GMEM_DDESHARE = 0x2000
+ ctypes.windll.user32.OpenClipboard(0)
+ ctypes.windll.user32.EmptyClipboard()
+ try:
+ # works on Python 2 (bytes() only takes one argument)
+ hCd = ctypes.windll.kernel32.GlobalAlloc(GMEM_DDESHARE, len(bytes(text))+1)
+ except TypeError:
+ # works on Python 3 (bytes() requires an encoding)
+ hCd = ctypes.windll.kernel32.GlobalAlloc(GMEM_DDESHARE, len(bytes(text, 'ascii'))+1)
+ pchData = ctypes.windll.kernel32.GlobalLock(hCd)
+ try:
+ # works on Python 2 (bytes() only takes one argument)
+ ctypes.cdll.msvcrt.strcpy(ctypes.c_char_p(pchData), bytes(text))
+ except TypeError:
+ # works on Python 3 (bytes() requires an encoding)
+ ctypes.cdll.msvcrt.strcpy(ctypes.c_char_p(pchData), bytes(text, 'ascii'))
+ ctypes.windll.kernel32.GlobalUnlock(hCd)
+ ctypes.windll.user32.SetClipboardData(1, hCd)
+ ctypes.windll.user32.CloseClipboard()
+
+def macSetClipboard(text):
+ text = str(text)
+ outf = os.popen('pbcopy', 'w')
+ outf.write(text)
+ outf.close()
+
+def macGetClipboard():
+ outf = os.popen('pbpaste', 'r')
+ content = outf.read()
+ outf.close()
+ return content
+
+def gtkGetClipboard():
+ return gtk.Clipboard().wait_for_text()
+
+def gtkSetClipboard(text):
+ global cb
+ text = str(text)
+ cb = gtk.Clipboard()
+ cb.set_text(text)
+ cb.store()
+
+def qtGetClipboard():
+ return str(cb.text())
+
+def qtSetClipboard(text):
+ text = str(text)
+ cb.setText(text)
+
+def xclipSetClipboard(text):
+ text = str(text)
+ outf = os.popen('xclip -selection c', 'w')
+ outf.write(text)
+ outf.close()
+
+def xclipGetClipboard():
+ outf = os.popen('xclip -selection c -o', 'r')
+ content = outf.read()
+ outf.close()
+ return content
+
+def xselSetClipboard(text):
+ text = str(text)
+ outf = os.popen('xsel -i', 'w')
+ outf.write(text)
+ outf.close()
+
+def xselGetClipboard():
+ outf = os.popen('xsel -o', 'r')
+ content = outf.read()
+ outf.close()
+ return content
+
+
+if os.name == 'nt' or platform.system() == 'Windows':
+ import ctypes
+ getcb = winGetClipboard
+ setcb = winSetClipboard
+elif os.name == 'mac' or platform.system() == 'Darwin':
+ getcb = macGetClipboard
+ setcb = macSetClipboard
+elif os.name == 'posix' or platform.system() == 'Linux':
+ xclipExists = os.system('which xclip') == 0
+ if xclipExists:
+ getcb = xclipGetClipboard
+ setcb = xclipSetClipboard
+ else:
+ xselExists = os.system('which xsel') == 0
+ if xselExists:
+ getcb = xselGetClipboard
+ setcb = xselSetClipboard
+ try:
+ import gtk
+ getcb = gtkGetClipboard
+ setcb = gtkSetClipboard
+ except Exception:
+ try:
+ import PyQt4.QtCore
+ import PyQt4.QtGui
+ app = PyQt4.QApplication([])
+ cb = PyQt4.QtGui.QApplication.clipboard()
+ getcb = qtGetClipboard
+ setcb = qtSetClipboard
+ except:
+ raise Exception('Pyperclip requires the gtk or PyQt4 module installed, or the xclip command.')
+copy = setcb
paste = getcb
View
132 rabinMiller.py
@@ -1,70 +1,62 @@
-# Primality Testing with the Rabin-Miller Algorithm
-# http://inventwithpython.com/hacking (BSD Licensed)
-
-import random
-
-def main():
- print('Example prime testing:')
- for num in (2, 3, 5, 10, 100, 101, 5099806053, 5099806057):
- print('%s is prime: %s' % (num, isPrime(num)))
-
-
-def rabinMiller(num):
- # Returns True if num is a prime number.
-
- s = num - 1
- t = 0
- while s % 2 == 0:
- # keep halving s until it is even (and use t
- # to count how many times we halve s)
- s = s // 2
- t += 1
-
- 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.
- i = 0
- while v != (num - 1):
- if i == t - 1:
- return False
- else:
- i = i + 1
- v = (v ** 2) % num
- return True
-
-
-def isPrime(num):
- # Return True if num is a prime number. This function does a quicker
- # prime number check before calling rabinMiller().
-
- # About a 1/3 of the time we can quickly determine if num is not prime
- # by dividing by the first few dozen prime numbers. This is quicker
- # than rabinMiller(), but unlike rabinMiller() is not guaranteed to
- # prove that a number is prime.
- lowPrimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]
-
- if (num < 2):
- return False
-
- if num in lowPrimes:
- return True
-
- # See if any of the low prime numbers can divide num
- for prime in lowPrimes:
- if (num % prime == 0):
- return False
-
- # If all else fails, call rabinMiller() to determine if num is a prime.
- return rabinMiller(num)
-
-
-def generateLargePrime(keysize=1024):
- # Return a random prime number of keysize bits in size.
- while True:
- n = random.randrange(2**(keysize-1), 2**(keysize))
- if isPrime(n) == True:
- return n
-
-if __name__ == '__main__':
- main()
+# Primality Testing with the Rabin-Miller Algorithm
+# http://inventwithpython.com/hacking (BSD Licensed)
+
+import random
+
+
+def rabinMiller(num):
+ # Returns True if num is a prime number.
+
+ s = num - 1
+ t = 0
+ while s % 2 == 0:
+ # keep halving s until it is even (and use t
+ # to count how many times we halve s)
+ s = s // 2
+ t += 1
+
+ for 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.
+ i = 0
+ while v != (num - 1):
+ if i == t - 1:
+ return False
+ else:
+ i = i + 1
+ v = (v ** 2) % num
+ return True
+
+
+def isPrime(num):
+ # Return True if num is a prime number. This function does a quicker
+ # prime number check before calling rabinMiller().
+
+ if (num < 2):
+ return False # 0, 1, and negative numbers are not prime
+
+ # About a 1/3 of the time we can quickly determine if num is not prime
+ # by dividing by the first few dozen prime numbers. This is quicker
+ # than rabinMiller(), but unlike rabinMiller() is not guaranteed to
+ # prove that a number is prime.
+ lowPrimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]
+
+ if num in lowPrimes:
+ return True
+
+ # See if any of the low prime numbers can divide num
+ for prime in lowPrimes:
+ if (num % prime == 0):
+ return False
+
+ # If all else fails, call rabinMiller() to determine if num is a prime.
+ return rabinMiller(num)
+
+
+def generateLargePrime(keysize=1024):
+ # Return a random prime number of keysize bits in size.
+ while True:
+ num = random.randrange(2**(keysize-1), 2**(keysize))
+ if isPrime(num) == True:
+ return num
View
318 rsaCipher.py
@@ -1,160 +1,160 @@
-# RSA Cipher
-# http://inventwithpython.com/hacking (BSD Licensed)
-
-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.)
-DEFAULT_BLOCK_SIZE = 128
-BYTE_SIZE = 256 # One byte has 256 different values.
-
-def main():
- # Runs a test that encrypts a message to a file or decrypts a message
- # from a file.
- filename = 'encrypted_file.txt'
- mode = 'encrypt' # set to 'encrypt' or 'decrypt'
-
- if mode == 'encrypt':
- message = '''"Journalists belong in the gutter because that is where the ruling classes throw their guilty secrets." -Gerald Priestland "The Founding Fathers gave the free press the protection it must have to bare the secrets of government and inform the people." -Hugo Black'''
- privKeyFilename = 'al_sweigart_privkey.txt'
- print('Encrypting and writing to %s...' % (filename))
- encryptedText = encryptAndWriteToFile(filename, privKeyFilename, message, 128)
-
- print('Encrypted text:')
- print(encryptedText)
-
- elif mode == 'decrypt':
- pubKeyFilename = 'al_sweigart_pubkey.txt'
- print('Reading from %s and decrypting...' % (filename))
- decryptedText = readFromFileAndDecrypt(filename, pubKeyFilename)
-
- print('Decrypted text:')
- print(decryptedText)
-
-
-def getBlocksFromText(message, blockSize=DEFAULT_BLOCK_SIZE):
- # Converts a string message to a list of block integers. Each integer
- # represents 512 (or whatever blockSize is set to) string characters.
-
- messageBytes = message.encode('ascii') # convert the string to bytes
- blockInts = []
- for blockStart in range(0, len(messageBytes), blockSize):
- # Calculate the block integer for this block of text
- blockInt = 0
- for i in range(blockStart, min(blockStart + blockSize, len(messageBytes))):
- blockInt += int(messageBytes[i]) * (BYTE_SIZE ** (i % blockSize))
- blockInts.append(blockInt)
- return blockInts
-
-
-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.
- 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.
- charNumber = blockInt // (BYTE_SIZE ** i)
- blockInt = blockInt % (BYTE_SIZE ** i)
- blockMessage.insert(0, bytes([charNumber]).decode('ascii'))
- message.extend(blockMessage)
- return ''.join(message)
-
-
-def encryptMessage(message, key, blockSize=DEFAULT_BLOCK_SIZE):
- # Converts the message string into a list of block integers, and then
- # encrypt each block integer. Pass the PUBLIC key to encrypt.
- encryptedBlocks = []
- n, e = key
-
- for block in getBlocksFromText(message, blockSize):
- # ciphertext = plaintext ^ e mod n
- encryptedBlocks.append(pow(block, e, n))
- return encryptedBlocks
-
-
-def decryptMessage(encryptedBlocks, messageLength, key, blockSize=DEFAULT_BLOCK_SIZE):
- # Decrypts a list of encrypted block ints into the original message
- # string. The original message length is required to properly decrypt
- # the last block. Be sure to pass the PRIVATE key to decrypt.
- decryptedBlocks = []
- n, d = key
- for block in encryptedBlocks:
- # plaintext = ciphertext ^ d mod n
- decryptedBlocks.append(pow(block, d, n))
- return getTextFromBlocks(decryptedBlocks, messageLength, blockSize)
-
-
-def readKeyFile(keyFilename):
- # Given the filename of a file that contains a public or private key,
- # return the key as a (n,e) or (n,d) tuple value.
- fp = open(keyFilename)
- content = fp.read()
- fp.close()
- keySize, N, EorD = content.split(',')
- return (int(keySize), int(N), int(EorD))
-
-
-def encryptAndWriteToFile(messageFilename, keyFilename, message, blockSize=DEFAULT_BLOCK_SIZE):
- # Using a key from a key file, encrypt the message and save it to a
- # file. Returns the encrypted message string.
- keySize, N, EorD = readKeyFile(keyFilename)
-
- # Check that key size is greater than block size.
- if keySize < blockSize * 8: # * 8 to convert bytes to bits
- sys.exit('ERROR: Block size is %s and key size is %s. The RSA cipher requires the block size to be less than the key size. Either decrease the block size or use different keys.' % (blockSize, keySize))
- key = (N, EorD)
-
- # Encrypt the message
- encryptedBlocks = encryptMessage(message, key, blockSize)
-
- # Convert the large int values to one string value.
- for i in range(len(encryptedBlocks)):
- encryptedBlocks[i] = str(encryptedBlocks[i])
- encryptedContent = ','.join(encryptedBlocks)
-
- # Write out the encrypted string to the output file.
- fp = open(messageFilename, 'w')
- fp.write('%s_%s_%s' % (len(message), blockSize, encryptedContent))
- fp.close()
-
- # Also return the encrypted string.
- return encryptedContent
-
-
-def readFromFileAndDecrypt(messageFilename, keyFilename):
- # Using a key from a key file, read an encrypted message from a file
- # and then decrypt it. Returns the decrypted message string.
- keySize, N, EorD = readKeyFile(keyFilename)
- key = (N, EorD)
-
- # Read in the message length and the encrypted message from the file.
- fp = open(messageFilename)
- content = fp.read()
- messageLength, blockSize, message = content.split('_')
- messageLength = int(messageLength)
- blockSize = int(blockSize)
-
- # Check that key size is greater than block size.
- if keySize < blockSize * 8: # * 8 to convert bytes to bits
- sys.exit('ERROR: Block size is %s and key size is %s. The RSA cipher requires the block size to be less than the key size. Did you specify the correct key file and encrypted file?' % (blockSize, keySize))
-
- # Convert the encrypted message into large int values.
- encryptedBlocks = []
- for block in message.split(','):
- encryptedBlocks.append(int(block))
-
- # Decrypt the large int values.
- return decryptMessage(encryptedBlocks, messageLength, key, blockSize)
-
-
-# If rsaCipher.py is run (instead of imported as a module) call
-# the main() function.
-if __name__ == '__main__':
+# RSA Cipher
+# http://inventwithpython.com/hacking (BSD Licensed)
+
+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.)
+DEFAULT_BLOCK_SIZE = 128
+BYTE_SIZE = 256 # One byte has 256 different values.
+
+def main():
+ # Runs a test that encrypts a message to a file or decrypts a message
+ # from a file.
+ filename = 'encrypted_file.txt'
+ mode = 'encrypt' # set to 'encrypt' or 'decrypt'
+
+ if mode == 'encrypt':
+ message = '''"Journalists belong in the gutter because that is where the ruling classes throw their guilty secrets." -Gerald Priestland "The Founding Fathers gave the free press the protection it must have to bare the secrets of government and inform the people." -Hugo Black'''
+ privKeyFilename = 'al_sweigart_privkey.txt'
+ print('Encrypting and writing to %s...' % (filename))
+ encryptedText = encryptAndWriteToFile(filename, privKeyFilename, message, 128)
+
+ print('Encrypted text:')
+ print(encryptedText)
+
+ elif mode == 'decrypt':
+ pubKeyFilename = 'al_sweigart_pubkey.txt'
+ print('Reading from %s and decrypting...' % (filename))
+ decryptedText = readFromFileAndDecrypt(filename, pubKeyFilename)
+
+ print('Decrypted text:')
+ print(decryptedText)
+
+
+def getBlocksFromText(message, blockSize=DEFAULT_BLOCK_SIZE):
+ # Converts a string message to a list of block integers. Each integer
+ # represents 512 (or whatever blockSize is set to) string characters.
+
+ messageBytes = message.encode('ascii') # convert the string to bytes
+ blockInts = []
+ for blockStart in range(0, len(messageBytes), blockSize):
+ # Calculate the block integer for this block of text
+ blockInt = 0
+ for i in range(blockStart, min(blockStart + blockSize, len(messageBytes))):
+ blockInt += int(messageBytes[i]) * (BYTE_SIZE ** (i % blockSize))
+ blockInts.append(blockInt)
+ return blockInts
+
+
+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.
+ 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.
+ charNumber = blockInt // (BYTE_SIZE ** i)
+ blockInt = blockInt % (BYTE_SIZE ** i)
+ blockMessage.insert(0, bytes([charNumber]).decode('ascii'))
+ message.extend(blockMessage)
+ return ''.join(message)
+
+
+def encryptMessage(message, key, blockSize=DEFAULT_BLOCK_SIZE):
+ # Converts the message string into a list of block integers, and then
+ # encrypt each block integer. Pass the PUBLIC key to encrypt.
+ encryptedBlocks = []
+ n, e = key
+
+ for block in getBlocksFromText(message, blockSize):
+ # ciphertext = plaintext ^ e mod n
+ encryptedBlocks.append(pow(block, e, n))
+ return encryptedBlocks
+
+
+def decryptMessage(encryptedBlocks, messageLength, key, blockSize=DEFAULT_BLOCK_SIZE):
+ # Decrypts a list of encrypted block ints into the original message
+ # string. The original message length is required to properly decrypt
+ # the last block. Be sure to pass the PRIVATE key to decrypt.
+ decryptedBlocks = []
+ n, d = key
+ for block in encryptedBlocks:
+ # plaintext = ciphertext ^ d mod n
+ decryptedBlocks.append(pow(block, d, n))
+ return getTextFromBlocks(decryptedBlocks, messageLength, blockSize)
+
+
+def readKeyFile(keyFilename):
+ # Given the filename of a file that contains a public or private key,
+ # return the key as a (n,e) or (n,d) tuple value.
+ fp = open(keyFilename)
+ content = fp.read()
+ fp.close()
+ keySize, N, EorD = content.split(',')
+ return (int(keySize), int(N), int(EorD))
+
+
+def encryptAndWriteToFile(messageFilename, keyFilename, message, blockSize=DEFAULT_BLOCK_SIZE):
+ # Using a key from a key file, encrypt the message and save it to a
+ # file. Returns the encrypted message string.
+ keySize, N, EorD = readKeyFile(keyFilename)
+
+ # Check that key size is greater than block size.
+ if keySize < blockSize * 8: # * 8 to convert bytes to bits
+ sys.exit('ERROR: Block size is %s and key size is %s. The RSA cipher requires the block size to be less than the key size. Either decrease the block size or use different keys.' % (blockSize, keySize))
+ key = (N, EorD)
+
+ # Encrypt the message
+ encryptedBlocks = encryptMessage(message, key, blockSize)
+
+ # Convert the large int values to one string value.
+ for i in range(len(encryptedBlocks)):
+ encryptedBlocks[i] = str(encryptedBlocks[i])
+ encryptedContent = ','.join(encryptedBlocks)
+
+ # Write out the encrypted string to the output file.
+ fp = open(messageFilename, 'w')
+ fp.write('%s_%s_%s' % (len(message), blockSize, encryptedContent))
+ fp.close()
+
+ # Also return the encrypted string.
+ return encryptedContent
+
+
+def readFromFileAndDecrypt(messageFilename, keyFilename):
+ # Using a key from a key file, read an encrypted message from a file
+ # and then decrypt it. Returns the decrypted message string.
+ keySize, N, EorD = readKeyFile(keyFilename)
+ key = (N, EorD)
+
+ # Read in the message length and the encrypted message from the file.
+ fp = open(messageFilename)
+ content = fp.read()
+ messageLength, blockSize, message = content.split('_')
+ messageLength = int(messageLength)
+ blockSize = int(blockSize)
+
+ # Check that key size is greater than block size.
+ if keySize < blockSize * 8: # * 8 to convert bytes to bits
+ sys.exit('ERROR: Block size is %s and key size is %s. The RSA cipher requires the block size to be less than the key size. Did you specify the correct key file and encrypted file?' % (blockSize, keySize))
+
+ # Convert the encrypted message into large int values.
+ encryptedBlocks = []
+ for block in message.split(','):
+ encryptedBlocks.append(int(block))
+
+ # Decrypt the large int values.
+ return decryptMessage(encryptedBlocks, messageLength, key, blockSize)
+
+
+# If rsaCipher.py is run (instead of imported as a module) call
+# the main() function.
+if __name__ == '__main__':
main()
View
152 simpleSubCipher.py
@@ -1,77 +1,77 @@
-# Simple Substitution Cipher
-# http://inventwithpython.com/hacking (BSD Licensed)
-
-import pyperclip, sys, random
-
-
-LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
-
-def main():
- myMessage = 'If a man is offered a fact which goes against his instincts, he will scrutinize it closely, and unless the evidence is overwhelming, he will refuse to believe it. If, on the other hand, he is offered something which affords a reason for acting in accordance to his instincts, he will accept it even on the slightest evidence. The origin of myths is explained in this way. -Bertrand Russell'
- myKey = 'LFWOAYUISVKMNXPBDCRJTQEGHZ'
- myMode = 'encrypt' # set to 'encrypt' or 'decrypt'
-
- checkValidKey(myKey)
-
- if myMode == 'encrypt':
- translated = encryptMessage(myKey, myMessage)
- elif myMode == 'decrypt':
- translated = decryptMessage(myKey, myMessage)
- print('Using key %s' % (myKey))
- print('The %sed message is:' % (myMode))
- print(translated)
- pyperclip.copy(translated)
- print()
- print('This message has been copied to the clipboard.')
-
-
-def checkValidKey(key):
- keyList = list(set(key))
- lettersList = list(LETTERS)
- keyList.sort()
- lettersList.sort()
- if keyList != lettersList or len(set(key)) != len(key):
- sys.exit('There is an error in the key or symbol set.')
-
-
-def translateMessage(key, message, mode):
- translated = ''
- CHARS_A = LETTERS
- CHARS_B = key
- if mode == 'decrypt':
- # For decrypting, we can use the same code as encrypting. We
- # just need to swap where the key and LETTERS strings are used.
- CHARS_A, CHARS_B = CHARS_B, CHARS_A
-
- # loop through each symbol in the message
- for symbol in message:
- if symbol.upper() in CHARS_A:
- # encrypt/decrypt the symbol
- symIndex = CHARS_A.find(symbol.upper())
- if symbol.isupper():
- translated += CHARS_B[symIndex].upper()
- else:
- translated += CHARS_B[symIndex].lower()
- else:
- # symbol is not in LETTERS, just add it
- translated += symbol
-
- return translated
-
-
-def encryptMessage(key, message):
- return translateMessage(key, message, 'encrypt')
-
-
-def decryptMessage(key, message):
- return translateMessage(key, message, 'decrypt')
-
-
-def getRandomKey():
- key = list(LETTERS)
- random.shuffle(key)
- return ''.join(key)
-
-
-if __name__ == '__main__':
+# Simple Substitution Cipher
+# http://inventwithpython.com/hacking (BSD Licensed)
+
+import pyperclip, sys, random
+
+
+LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+
+def main():
+ myMessage = 'If a man is offered a fact which goes against his instincts, he will scrutinize it closely, and unless the evidence is overwhelming, he will refuse to believe it. If, on the other hand, he is offered something which affords a reason for acting in accordance to his instincts, he will accept it even on the slightest evidence. The origin of myths is explained in this way. -Bertrand Russell'
+ myKey = 'LFWOAYUISVKMNXPBDCRJTQEGHZ'
+ myMode = 'encrypt' # set to 'encrypt' or 'decrypt'
+
+ checkValidKey(myKey)
+
+ if myMode == 'encrypt':
+ translated = encryptMessage(myKey, myMessage)
+ elif myMode == 'decrypt':
+ translated = decryptMessage(myKey, myMessage)
+ print('Using key %s' % (myKey))
+ print('The %sed message is:' % (myMode))
+ print(translated)
+ pyperclip.copy(translated)
+ print()
+ print('This message has been copied to the clipboard.')
+
+
+def checkValidKey(key):
+ keyList = list(key)
+ lettersList = list(LETTERS)
+ keyList.sort()
+ lettersList.sort()
+ if keyList != lettersList:
+ sys.exit('There is an error in the key or symbol set.')
+
+
+def encryptMessage(key, message):
+ return translateMessage(key, message, 'encrypt')
+
+
+def decryptMessage(key, message):
+ return translateMessage(key, message, 'decrypt')
+
+
+def translateMessage(key, message, mode):
+ translated = ''
+ charsA = LETTERS
+ charsB = key
+ if mode == 'decrypt':
+ # For decrypting, we can use the same code as encrypting. We
+ # just need to swap where the key and LETTERS strings are used.
+ charsA, charsB = charsB, charsA
+
+ # loop through each symbol in the message
+ for symbol in message:
+ if symbol.upper() in charsA:
+ # encrypt/decrypt the symbol
+ symIndex = charsA.find(symbol.upper())
+ if symbol.isupper():
+ translated += charsB[symIndex].upper()
+ else:
+ translated += charsB[symIndex].lower()
+ else:
+ # symbol is not in LETTERS, just add it
+ translated += symbol
+
+ return translated
+
+
+def getRandomKey():
+ key = list(LETTERS)
+ random.shuffle(key)
+ return ''.join(key)
+
+
+if __name__ == '__main__':
main()
View
512 simpleSubHacker.py
@@ -1,279 +1,235 @@
-# Simple Substitution Cipher Hacker
-# http://inventwithpython.com/hacking (BSD Licensed)
-
-"""
-In this program, a "word pattern" is a description of which letters are
-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)
-
-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.
-"""
-
-# The import statement for wordPatterns is further down.
-import os, simpleSubCipher, re, copy
-
-LETTERS = simpleSubCipher.LETTERS
-
-
-def main():
- message = 'SY L NLX SR PYYACAO L YLWJ EISWI UPAR LULSXRJ ISR SXRJSXWJR, IA ESMM RWCTJSXSZA SJ WMPRAMH, LXO TXMARR JIA AQSOAXWA SR PQACEIAMNSXU, IA ESMM CAYTRA JP FAMSAQA SJ. SY, PX JIA PJIAC ILXO, IA SR PYYACAO RPNAJISXU EISWI LYYPCOR L CALRPX YPC LWJSXU SX LWWPCOLXWA JP ISR SXRJSXWJR, IA ESMM LWWABJ SJ AQAX PX JIA RMSUIJARJ AQSOAXWA. JIA PCSUSX PY NHJIR SR AGBMLSXAO SX JISR ELH. -FACJCLXO CTRRAMM'
-
- NONLETTERSPATTERN = re.compile('[^A-Z\s]')
- ciphertext = NONLETTERSPATTERN.sub('', message.upper()).split()
-
- # allCandidates is a dict with keys of a single ciphertext word, and
- # values of the possible word patterns
- # e.g. allCandidates == {'PYYACAO': ['alleged', 'ammeter', ...etc],
- # 'EISWI': ['aerie', 'aging', 'algol', ...etc],
- # 'LULSXRJ': ['abalone', 'abashed', ...etc],
- # ...etc }
- allCandidates = {}
- for cipherWord in ciphertext:
- pattern = getWordPattern(cipherWord)
- if pattern not in wordPatterns.allPatterns:
- continue
- allCandidates[cipherWord] = copy.copy(wordPatterns.allPatterns[pattern])
-
- # convert candidate words to uppercase
- for i in range(len(allCandidates[cipherWord])):
- allCandidates[cipherWord][i] = allCandidates[cipherWord][i].upper()
-
- # determine the possible valid ciphertext translations
- print('Hacking...')
- # 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.)')
- theMap = hackSimpleSub(getNewBlankMapping(), allCandidates)
-
- # display the results to the user.
- print('Done.')
- print()
- printMapping(theMap)
- print()
- print('Original ciphertext:')
- print(message)
- print()
- print('Hacked message:')
- print(decryptWithMap(message, theMap))
- print()
-
-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'
- word = word.upper()
- nextNum = 0
- letterNums = {}
- wordPattern = []
-
- for letter in word:
- if letter in letterNums:
- wordPattern.append(str(letterNums[letter]))
- else:
- wordPattern.append(str(nextNum))
- letterNums[letter] = nextNum
- nextNum += 1
- return '.'.join(wordPattern)
-
-
-
-def hackSimpleSub(theMap, allCandidates):
- # allCandidate's format:
- # { 'cipherword1': ['candidate1a', 'candidate1b', ...],
- # 'cipherword2': ['candidate2a', 'candidate2b', ...],
- # ...}
-
- for cipherWord in allCandidates.keys():
- # get a new mapping for each ciphertext word
- newMap = getNewBlankMapping()
-
- # create a map that has all the letters' possible candidate
- # decryptions added to it
- for candidate in allCandidates[cipherWord]:
- newMap = addMappings(newMap, cipherWord, candidate)
-
- # intersect this new map with the existing map
- theMap = intersectMappings(theMap, newMap)
-
- # remove any solved letters from the other possible mappings
- theMap = removeSolvedLettersFromMapping(theMap)
-
- return theMap
-
-
-def getNewBlankMapping():
- # Returns a dict where the keys are single-character strings of the
- # uppercase letters, and the values are blank lists.
- # E.g. {'A': [], 'B': [], 'C': [], ...etc}
- theMap = {}
- for i in LETTERS:
- theMap[i] = []
- return theMap
-
-
-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 cipherWord parameter is a string value of the ciphertext word.
- # 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.
-
- for i in range(len(cipherWord)):
- if candidate[i] not in theMap[cipherWord[i]]:
- theMap[cipherWord[i]].append(candidate[i])
- return theMap
-
-
-def intersectMappings(mapA, mapB):
- # To intersect two maps, create a blank map, and that add only the
- # candidate decryption letters if they exist in both maps.
- result = getNewBlankMapping()
- for i in mapA.keys():
-
- # 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] == []:
- result[i] = copy.copy(mapA[i])
-
- else:
- for j in mapA[i]:
- if j in mapB[i]:
- result[i].append(j)
- return result
-
-
-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.)
- previousSolvedLetters = []
- solvedLetters = None
- while previousSolvedLetters != solvedLetters:
- # This loop will break when solvedLetters is not changed by the
- # reduction process (and is the same as previousSolvedLetters).
- previousSolvedLetters = solvedLetters
- solvedLetters = []
-
- # 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.
- 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.
- return theMap
-
-
-def printMapping(theMap):
- # Display a mapping data structure on the screen.
- print('Mapping:')
- print(' ' + ' '.join(list(LETTERS)))
- print(' ' + ' '.join('=' * len(LETTERS)))
-
- for i in range(len(LETTERS)):
- print(' ', end='')
- foundAnyLetters = False
- for j in LETTERS:
- # theMap[j] points to a list of single-character strings that
- # are potential solutions for the ciphertext letter in j.
- if len(theMap[j]) > i:
- foundAnyLetters = True
- print(theMap[j][i] + ' ', end='')
- else:
- print(' ', end='')
- print()
- if foundAnyLetters == False:
- break
-
-
-def decryptWithMap(ciphertext, theMap):
- # This function will do a simple sub decryption of ciphertext with the
- # information in theMap, instead of a simple sub key.
-
- # First create a simple sub key from the theMap mapping.
- key = ['x'] * len(LETTERS)
- for letter in theMap.keys():
- if len(theMap[letter]) == 1:
- # If only one possible letter mapping, add it to the key.
- keyIndex = LETTERS.find(theMap[letter][0].upper())
- key[keyIndex] = letter.upper()
- else:
- ciphertext = ciphertext.replace(letter, '_')
- key = ''.join(key)
-
- # Then decrypt the original ciphertext with this key and return the
- # decryption.
- 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.
- # (Download this file from http://invpy.com/dictionary.txt)
- if not os.path.exists('wordPatterns.py'):
- import pprint # import the "pretty print" module
- allPatterns = {}
-
- fp = open('dictionary.txt')
- wordList = fp.readlines()
- fp.close()
-
- for word in wordList:
- word = word.strip() # get rid of newline at the end
- pattern = getWordPattern(word)
-
- if pattern not in allPatterns:
- allPatterns[pattern] = [word]
- else:
- allPatterns[pattern].append(word)
-
- # This is code that writes code. The wordPatterns.py file contains
- # one very, very large assignment statement.
- fp = open('wordPatterns.py', 'w')
- fp.write('allPatterns = ')
- fp.write(pprint.pformat(allPatterns))
- fp.close()
-
-# Import our wordPatterns.py file.
-checkForWordPatternsPy()
-import wordPatterns
-
-if __name__ == '__main__':
+# Simple Substitution Cipher Hacker
+# http://inventwithpython.com/hacking (BSD Licensed)
+
+"""
+In this program, a "word pattern" is a description of which letters are
+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)
+
+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.
+"""
+
+import os, simpleSubCipher, re, copy, makeWordPatterns
+
+if not os.path.exists('wordPatterns.py'):
+ makeWordPatterns.main() # create the wordPatterns.py file
+import wordPatterns
+
+LETTERS = simpleSubCipher.LETTERS
+
+
+def main():
+ message = 'Sy l nlx sr pyyacao l ylwj eiswi upar lulsxrj isr sxrjsxwjr, ia esmm rwctjsxsza sj wmpramh, lxo txmarr jia aqsoaxwa sr pqaceiamnsxu, ia esmm caytra jp famsaqa sj. Sy, px jia pjiac ilxo, ia sr pyyacao rpnajisxu eiswi lyypcor l calrpx ypc lwjsxu sx lwwpcolxwa jp isr sxrjsxwjr, ia esmm lwwabj sj aqax px jia rmsuijarj aqsoaxwa. Jia pcsusx py nhjir sr agbmlsxao sx jisr elh. -Facjclxo Ctrramm'
+
+ NONLETTERSPATTERN = re.compile('[^A-Z\s]')
+ ciphertext = NONLETTERSPATTERN.sub('', message.upper()).split()
+
+ # allCandidates is a dict with keys of a single ciphertext word, and
+ # values of the possible word patterns
+ # e.g. allCandidates == {'PYYACAO': ['alleged', 'ammeter', ...etc],
+ # 'EISWI': ['aerie', 'aging', 'algol', ...etc],
+ # 'LULSXRJ': ['abalone', 'abashed', ...etc],
+ # ...etc }
+ allCandidates = {}
+ for cipherWord in ciphertext:
+ pattern = makeWordPatterns.getWordPattern(cipherWord)
+ if pattern not in wordPatterns.allPatterns:
+ continue
+ allCandidates[cipherWord] = copy.copy(wordPatterns.allPatterns[pattern])
+
+ # convert candidate words to uppercase
+ for i in range(len(allCandidates[cipherWord])):
+ allCandidates[cipherWord][i] = allCandidates[cipherWord][i].upper()
+
+ # determine the possible valid ciphertext translations
+ print('Hacking...')
+ # 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.)')
+ theMap = hackSimpleSub(getBlankMapping(), allCandidates)
+
+ # display the results to the user.
+ print('Done.')
+ print()
+ printMapping(theMap)
+ print()
+ print('Original ciphertext:')
+ print(message)
+ print()
+ print('Hacked message:')
+ print(decryptWithMap(message, theMap))
+ print()
+
+
+def hackSimpleSub(theMap, allCandidates):
+ # allCandidate's format:
+ # { 'cipherword1': ['candidate1a', 'candidate1b', ...],
+ # 'cipherword2': ['candidate2a', 'candidate2b', ...],
+ # ...}
+
+ for cipherWord in allCandidates.keys():
+ # get a new mapping for each ciphertext word
+ newMap = getBlankMapping()
+
+ # create a map that has all the letters' possible candidate
+ # decryptions added to it
+ for candidate in allCandidates[cipherWord]:
+ newMap = addLettersToMapping(newMap, cipherWord, candidate)
+
+ # intersect this new map with the existing map
+ theMap = intersectMappings(theMap, newMap)
+
+ # remove any solved letters from the other possible mappings
+ theMap = removeSolvedLettersFromMapping(theMap)
+
+ return theMap
+
+
+def getBlankMapping():
+ # Returns a dict where the keys are single-character strings of the
+ # uppercase letters, and the values are blank lists.
+ # E.g. {'A': [], 'B': [], 'C': [], ...etc}
+ theMap = {}
+ for letter in LETTERS:
+ theMap[letter] = []
+ return theMap
+
+
+def addLettersToMapping(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 cipherWord parameter is a string value of the ciphertext word.
+ # 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.
+
+ for i in range(len(cipherWord)):
+ if candidate[i] not in theMap[cipherWord[i]]:
+ theMap[cipherWord[i]].append(candidate[i])
+ return theMap
+
+
+def intersectMappings(mapA, mapB):
+ # To intersect two maps, create a blank map, and that add only the
+ # candidate decryption letters if they exist in both maps.
+ intersectedMap = getBlankMapping()
+ for letter in mapA.keys():
+
+ # An empty list means "any letter is possible". So just copy the
+ # other map entirely.
+ if mapA[letter] == []:
+ intersectedMap[letter] = copy.copy(mapB[letter])
+ elif mapB[letter] == []:
+ intersectedMap[letter] = copy.copy(mapA[letter])
+
+ else:
+ # If a letter in mapA[letter] exists in mapB[letter], add
+ # that letter to intersectedMap[letter].
+ for mappedLetter in mapA[letter]:
+ if mappedLetter in mapB[letter]:
+ intersectedMap[letter].append(mappedLetter)
+ return intersectedMap
+
+
+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.)
+ previousSolvedLetters = []
+ solvedLetters = None
+ while previousSolvedLetters != solvedLetters:
+ # This loop will break when solvedLetters is not changed by the
+ # reduction process (and is the same as previousSolvedLetters).
+ previousSolvedLetters = solvedLetters
+ solvedLetters = []
+
+ # 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.
+ 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.
+ return theMap
+
+
+def printMapping(theMap):
+ # Display a mapping data structure on the screen.
+ print('Mapping:')
+ print(' ' + ' '.join(list(LETTERS)))
+ print(' ' + ' '.join('=' * len(LETTERS)))
+
+ for i in range(len(LETTERS)):
+ print(' ', end='')
+ foundAnyLetters = False
+ for j in LETTERS:
+ # theMap[j] points to a list of single-character strings that
+ # are potential solutions for the ciphertext letter in j.
+ if len(theMap[j]) > i:
+ foundAnyLetters = True
+ print(theMap[j][i] + ' ', end='')
+ else:
+ print(' ', end='')
+ print()
+ if foundAnyLetters == False:
+ break
+
+
+def decryptWithMap(ciphertext, theMap):
+ # This function will do a simple sub decryption of ciphertext with the
+ # information in theMap, instead of a simple sub key.
+
+ # First create a simple sub key from the theMap mapping.
+ key = ['x'] * len(LETTERS)
+ for letter in theMap.keys():
+ if len(theMap[letter]) == 1:
+ # If only one possible letter mapping, add it to the key.
+ keyIndex = LETTERS.find(theMap[letter][0].upper())
+ key[keyIndex] = letter.upper()
+ else:
+ ciphertext = ciphertext.replace(letter, '_')
+ key = ''.join(key)
+
+ # Then decrypt the original ciphertext with this key and return the
+ # decryption.
+ return simpleSubCipher.decryptMessage(key, ciphertext)
+
+
+if __name__ == '__main__':
main()

0 comments on commit fb8aa2e

Please sign in to comment.