Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 75 lines (61 sloc) 4.433 kB
f16b361 @asweigart Initial commit.
authored
1 # Null Cipher Breaker, http://inventwithpython.com/codebreaker (BSD Licensed)
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
2 import copy, time, nullCipher, pyperclip, transpositionBreaker
f16b361 @asweigart Initial commit.
authored
3
4 # There are two settings our breaking program needs to limit the range of the possible keys it checks.
5 # 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.
6 # 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'
7 # 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.
8 MAX_KEY_NUMBER = 9
9 MAX_KEY_LENGTH = 5
10
11 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.'
12 #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.
13
14 def main():
15 # 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.
16 possibleKeys = 0 # start the number of keys at 0.
17 for i in range(1, MAX_KEY_LENGTH + 1):
18 # 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.
19 # 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.
20 possibleKeys += MAX_KEY_NUMBER ** i
21
22 # 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.
23 print('Max key number: %s' % MAX_KEY_NUMBER)
24 print('Max key length: %s' % MAX_KEY_LENGTH)
25 print('Possible keys to try: %s' % (possibleKeys))
26 print()
27
28 # Python programs can be stopped at any time by pressing Ctrl-C (on Windows) or Ctrl-D (on Mac and Linux)
29 print('(Press Ctrl-C or Ctrl-D to quit at any time.)')
30 print('Breaking...')
31
32 # The breakNull() function will have all the encryption breaking code in it, and return the original plaintext.
33 brokenMessage = breakNull(myMessage)
34
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
35 if brokenMessage != None:
f16b361 @asweigart Initial commit.
authored
36 # The plaintext is displayed on the screen. For the convenience of the user, we copy the text of the code to the clipboard.
37 print('Copying broken message to clipboard:')
38 print(brokenMessage)
39 pyperclip.copy(brokenMessage)
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
40 else:
41 print('Failed to break encryption.')
f16b361 @asweigart Initial commit.
authored
42
43
44 def breakNull(ciphertext):
45 # 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.
46 # This is because the key '1 0' will decrypt differently than '1 0 0'.
47 for trialKeyLength in range(1, MAX_KEY_LENGTH + 1):
48 # 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.
49 # We use list replication (multiplying a list value by an int value) to get the starting key.
50 trialKey = [0] * trialKeyLength
51
52 trialKey = getNextKey(trialKey)
53
54 while trialKey != [0] * trialKeyLength:
55 decryptedText = nullCipher.decryptMessage(ciphertext, trialKey)
56 percentEnglish = round(transpositionBreaker.getEnglishCount(decryptedText) * 100, 2)
57 if percentEnglish > 0:
58 print('Key %s decrypts to %s%% English.' % (trialKey, percentEnglish))
59 if percentEnglish >= 25:
60 print()
61 print('Possible encryption break:')
62 print('Key ' + str(trialKey) + ': ' + decryptedText[:100])
63 print()
64 print('Enter D for done, or just press Enter to continue breaking:')
65 response = input('> ')
66
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
67 if response.strip().upper().startswith('D'):
f16b361 @asweigart Initial commit.
authored
68 return decryptedText
69 trialKey = getNextKey(trialKey)
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
70 print('Failed to break encryption.')
f16b361 @asweigart Initial commit.
authored
71 return None
72
73
74 if __name__ == '__main__':
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
75 main()
Something went wrong with that request. Please try again.