Permalink
Browse files

Changes made while making unit tests.

  • Loading branch information...
asweigart committed Aug 28, 2012
1 parent e280220 commit d95e7daa7c0ce27df94ca911dc4890f7d3e1c9bb
Showing with 200 additions and 89 deletions.
  1. +1 −1 affineBreaker.py
  2. +1 −0 affineCipher.py
  3. +136 −4 codebreaker_unit_tests.py
  4. +21 −20 nullBreaker.py
  5. +2 −2 nullCipher.py
  6. +5 −36 pyperclip.py
  7. +1 −0 simpleSubCipher.py
  8. +3 −2 simpleSubKeyword.py
  9. +23 −17 vigenereBreaker.py
  10. +7 −7 vigenereCipher.py
View
@@ -14,7 +14,7 @@ def main():
# 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 ciphertext to clipboard:')
- print(brokenCiphertext[:1000])
+ print(brokenCiphertext)
pyperclip.copy(brokenCiphertext)
else:
print('Failed to break encryption.')
View
@@ -46,6 +46,7 @@ def encryptMessage(keyA, keyB, message):
sys.exit('The affine cipher becomes incredibly weak when keyA is set to 1. Choose a different key.')
if keyB == 0:
sys.exit('The affine cipher becomes incredibly weak when keyB is set to 0. Choose a different key.')
+
if gcd(keyA, len(LETTERS)) != 1:
sys.exit('The key (%s) and the size of the alphabet (%s) are not relatively prime. Choose a different key.' % (keyA, len(LETTERS)))
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -1,7 +1,7 @@
# Null Cipher Breaker
# http://inventwithpython.com/codebreaker (BSD Licensed)
-import nullCipher, pyperclip, detectEnglish
+import nullCipher, pyperclip, detectEnglish, itertools, sys
# 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.
@@ -10,8 +10,10 @@
MAX_KEY_NUMBER = 9
MAX_KEY_LENGTH = 5
-myMessage = 'kWhhbe#n n>IP uTksEe b<aZ wXCo(rdq7,( iActy moveeanggsU jCku2stmT dwhlvaPt FZIx czyhtoo(&sxe SUi6t Ylt#o 3kmCeaU5nb -rR-b nbLegitOTh6eroN Jmogzr2e Lgnpor/0 GleOjs.s.'
- #W h e n I u s e a w o rd , i t m eans just what I choose it to mean -- neither more nor less.
+SILENT_MODE = False
+
+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.
@@ -46,31 +48,30 @@ 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 trialKeyLength in range(1, MAX_KEY_LENGTH + 1):
- # We will be using the "list of int values" for of keys. The string form like '4 2 3' that we use in the original encryption program is used just because it makes it easier to type for the user.
- # We use list replication (multiplying a list value by an int value) to get the starting key.
- trialKey = [0] * trialKeyLength
-
- trialKey = getNextKey(trialKey)
-
- while trialKey != [0] * trialKeyLength:
- decryptedText = nullCipher.decryptMessage(ciphertext, trialKey)
- percentEnglish = round(detectEnglish.getEnglishCount(decryptedText) * 100, 2)
- if percentEnglish > 0:
- print('Key %s decrypts to %s%% English.' % (trialKey, percentEnglish))
- if percentEnglish >= 25:
+ for keyLength in range(1, MAX_KEY_LENGTH + 1):
+ for keyParts in itertools.product(range(MAX_KEY_NUMBER + 1), repeat=keyLength):
+ key = []
+ for digit in keyParts:
+ key.append(str(digit))
+ key = ''.join(key)
+
+ decryptedText = nullCipher.decryptMessage(key, ciphertext)
+
+ if not SILENT_MODE:
+ print('Key %s: %s' % (key, decryptedText[:40]))
+
+ if detectEnglish.isEnglish(decryptedText):
print()
print('Possible encryption break:')
- print('Key ' + str(trialKey) + ': ' + decryptedText[:100])
+ print('Key: %s' % (key))
+ print('Decrypted message: ' + decryptedText[:200])
print()
print('Enter D for done, or just press Enter to continue breaking:')
response = input('> ')
if response.strip().upper().startswith('D'):
return decryptedText
- trialKey = getNextKey(trialKey)
- print('Failed to break encryption.')
- return None
+ return None # failed to break encryption
if __name__ == '__main__':
View
@@ -22,7 +22,7 @@ def main():
pyperclip.copy(translated)
-def encryptMessage(message, key):
+def encryptMessage(key, message):
# The expression int(key[keyIndex]) will be used to decide how many
# nulls should be inserted. For example, if key is the value '102' and
# keyIndex is 0, then 1 null character will be inserted into the
@@ -56,7 +56,7 @@ def encryptMessage(message, key):
return ciphertext
-def decryptMessage(message, key):
+def decryptMessage(key, message):
# The value inside messageIndex will refer to the index we are
# currently looking at in message.
messageIndex = 0
View
@@ -1,37 +1,5 @@
# Pyperclip v1.4
-
-
-"""TODO - need to update to fall back on TK
-
-from Tkinter import Tk
-r = Tk()
-r.withdraw()
-r.clipboard_clear()
-r.clipboard_append('i can has clipboardz?')
-print r.clipboard_get()
-r.destroy()
-
-
-Do performance testing on other OSes to see if this approach beats out the others.
-"""
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
# A cross-platform clipboard module for Python. (only handles plain text for now)
# By Al Sweigart al@coffeeghost.net
@@ -75,7 +43,7 @@
# 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
+import platform, os, sys
def winGetClipboard():
ctypes.windll.user32.OpenClipboard(0)
@@ -103,7 +71,7 @@ def winSetClipboard(text):
# 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.SetClipboardData(1, hCd)
ctypes.windll.user32.CloseClipboard()
def macSetClipboard(text):
@@ -121,6 +89,7 @@ def gtkGetClipboard():
return gtk.Clipboard().wait_for_text()
def gtkSetClipboard(text):
+ global cb
cb = gtk.Clipboard()
cb.set_text(text)
cb.store()
@@ -175,11 +144,11 @@ def xselGetClipboard():
import gtk
getcb = gtkGetClipboard
setcb = gtkSetClipboard
- except:
+ except Exception:
try:
import PyQt4.QtCore
import PyQt4.QtGui
- app = QApplication([])
+ app = PyQt4.QApplication([])
cb = PyQt4.QtGui.QApplication.clipboard()
getcb = qtGetClipboard
setcb = qtSetClipboard
View
@@ -36,6 +36,7 @@ def checkValidKey(key):
def translateMessage(key, message, mode):
translated = ''
+ message = message.upper()
SET_A = LETTERS
SET_B = key
View
@@ -4,7 +4,7 @@
import pyperclip, simpleSubCipher
def main():
- myMessage = r"""This is my secret message."""
+ myMessage = r"""Your cover is blown."""
myKey = 'alphanumeric'
myMode = 'encrypt' # set to 'encrypt' or 'decrypt'
@@ -38,7 +38,8 @@ def decryptMessage(key, message):
def makeSimpleSubKey(keyword):
# create the key from the keyword
newKey = ''
- keyAlphabet = list(' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
+ keyword = keyword.upper()
+ keyAlphabet = list(simpleSubCipher.LETTERS)
for i in range(len(keyword)):
if keyword[i] not in newKey:
newKey += keyword[i]
View
@@ -134,26 +134,31 @@ 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.
- print('Determining most likely key lengths with Kasiski Examination...')
+ if not SILENT_MODE:
+ print('Determining most likely key lengths with Kasiski Examination...')
+
allLikelyKeyLengths = kasiskiExamination(ciphertext)
- print('Kasiski Examination results say the most likely key lengths are: ', end='')
- for keyLength in allLikelyKeyLengths:
- print('%s ' % (keyLength), end='')
- print()
- print()
+ if not SILENT_MODE:
+ print('Kasiski Examination results say the most likely key lengths are: ', end='')
+ for keyLength in allLikelyKeyLengths:
+ print('%s ' % (keyLength), end='')
+ print()
+ 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 brokenCiphertext == None:
- print('Unable to break message with likely key length(s). Brute forcing key length...')
+ 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
- print('Attempting break with key length %s (%s possible keys)...' % (keyLength, NUM_MOST_FREQ_LETTERS ** keyLength))
+ if not SILENT_MODE:
+ print('Attempting break with key length %s (%s possible keys)...' % (keyLength, NUM_MOST_FREQ_LETTERS ** keyLength))
brokenCiphertext = attemptBreakWithKeyLength(ciphertext, keyLength)
if brokenCiphertext != None:
break
@@ -200,7 +205,7 @@ def attemptBreakWithKeyLength(ciphertext, mostLikelyKeyLength):
# match. See the englishFreqMatch() comments in freqFinder).
freqScores = []
for possibleKey in LETTERS:
- translated = vigenereCipher.decryptMessage(nthLetters, possibleKey)
+ translated = vigenereCipher.decryptMessage(possibleKey, nthLetters)
freqScores.append((possibleKey, freqFinder.englishFreqMatch(translated)))
# Each value in freqScores is a tuple (<letter>, <match score>). Since
@@ -210,12 +215,13 @@ def attemptBreakWithKeyLength(ciphertext, mostLikelyKeyLength):
allFreqScores.append(freqScores[:NUM_MOST_FREQ_LETTERS])
- for i in range(len(allFreqScores)):
- # use i+1, because otherwise the "first" letter is 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='')
- print()
+ if not SILENT_MODE:
+ for i in range(len(allFreqScores)):
+ # use i+1, because otherwise the "first" letter is 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='')
+ print()
# Try every combination of the most likely letters for each position
# in the key.
@@ -228,7 +234,7 @@ def attemptBreakWithKeyLength(ciphertext, mostLikelyKeyLength):
if not SILENT_MODE:
print('Attempting with key: %s' % (possibleKey))
- decryptedText = vigenereCipher.decryptMessage(ciphertext, possibleKey)
+ decryptedText = vigenereCipher.decryptMessage(possibleKey, ciphertext)
if freqFinder.englishTrigramMatch(decryptedText):
if detectEnglish.isEnglish(decryptedText):
View
@@ -14,9 +14,9 @@ def main():
key = key.upper()
if mode == 'encrypt':
- translated = translateMessage(message, key, 'encrypt')
+ translated = encryptMessage(key, message)
elif mode == 'decrypt':
- translated = translateMessage(message, key, 'decrypt')
+ translated = decryptMessage(key, message)
print('%sed message:' % (mode.title()))
print(translated)
@@ -25,15 +25,15 @@ def main():
print('The message has been copied to the clipboard.')
-def encryptMessage(message, key):
- return translateMessage(message, key, 'encrypt')
+def encryptMessage(key, message):
+ return translateMessage(key, message, 'encrypt')
-def decryptMessage(message, key):
- return translateMessage(message, key, 'decrypt')
+def decryptMessage(key, message):
+ return translateMessage(key, message, 'decrypt')
-def translateMessage(message, key, mode):
+def translateMessage(key, message, mode):
translated = [] # stores the encrypted/decrypted message string
keyIndex = 0

0 comments on commit d95e7da

Please sign in to comment.