Fixed typo
[cipher-tools.git] / cipher.py
1 import string
2 import collections
3
4 english_counts = collections.defaultdict(int)
5 with open('count_1l.txt', 'r') as f:
6 for line in f:
7 (letter, count) = line.split("\t")
8 english_counts[letter] = int(count)
9
10 modular_division = [[0]* 26 for i in range(26)]
11 for i in range(26):
12 for j in range(26):
13 t = (i*j) % 26
14 # therefore, i = t / j
15 modular_division[t][j] = i
16
17
18 def sanitise(text):
19 sanitised = [c.lower() for c in text if c in string.ascii_letters]
20 return ''.join(sanitised)
21
22 def letter_frequencies(message):
23 frequencies = collections.defaultdict(int)
24 for letter in sanitise(message):
25 frequencies[letter]+=1
26 return frequencies
27
28 def scale_freq(frequencies):
29 total= sum(frequencies.values())
30 scaled_frequencies = collections.defaultdict(int)
31 for letter in frequencies.keys():
32 scaled_frequencies[letter] = frequencies[letter] / total
33 return scaled_frequencies
34
35 def value_diff(frequencies1, frequencies2):
36 total= 0
37 for letter in frequencies1.keys():
38 total += abs(frequencies1[letter]-frequencies2[letter])
39 return total
40
41
42
43 def caesar_cipher_letter(letter, shift):
44 if letter in string.ascii_letters:
45 if letter in string.ascii_lowercase:
46 return chr((ord(letter) - ord('a') + shift) % 26 + ord('a'))
47 else:
48 new_letter = letter.lower()
49 yolo = chr((ord(new_letter) - ord('a') + shift) % 26 + ord('a'))
50 return yolo.upper()
51 else:
52 return letter
53
54 def caesar_decipher_letter(letter, shift):
55 return caesar_cipher_letter(letter, -shift)
56
57 def caesar_cipher_message(message, shift):
58 big_cipher = [caesar_cipher_letter(l, shift) for l in message]
59 return ''.join(big_cipher)
60
61 def caesar_decipher_message(message, shift):
62 return caesar_cipher_message(message, -shift)
63
64 def affine_cipher_letter(letter, multiplier, shift, one_based=True):
65 if letter in string.ascii_letters:
66 if letter in string.ascii_lowercase:
67 alphastart = ord('a')
68 else:
69 alphastart = ord('A')
70 letter_number = ord(letter) - alphastart
71 if one_based: letter_number += 1
72 enciphered_letter_number = letter_number * multiplier + shift
73 if one_based: enciphered_letter_number -=1
74 enciphered_letter = chr(enciphered_letter_number % 26 + alphastart)
75 return enciphered_letter
76 else:
77 return letter
78
79 def affine_decipher_letter(letter, multiplier, shift, one_based=True):
80 if letter in string.ascii_letters:
81 if letter in string.ascii_lowercase:
82 alphastart = ord('a')
83 else:
84 alphastart = ord('A')
85 letter_number = ord(letter) - alphastart
86 if one_based: letter_number +=1
87 after_unshift = letter_number - shift
88 deciphered_letter_number = modular_division[after_unshift % 26][multiplier]
89 if one_based: deciphered_letter_number -=1
90 deciphered_letter = chr(deciphered_letter_number % 26 + alphastart)
91 return deciphered_letter
92 else:
93 return letter
94
95 def affine_cipher_message(message, multiplier, shift, one_based=True):
96 big_cipher = [affine_cipher_letter(l, multiplier, shift, one_based) for l in message]
97 return ''.join(big_cipher)
98
99 def affine_decipher_message(message, multiplier, shift, one_based=True):
100 big_decipher = [affine_decipher_letter(l, multiplier, shift, one_based) for l in message]
101 return ''.join(big_decipher)
102
103
104 def caesar_break(message):
105 best_key = 0
106 best_fit = float("inf")
107 for shift in range(26):
108 plaintxt = caesar_decipher_message(message, shift)
109 lettertxt = letter_frequencies(plaintxt)
110 total1 = scale_freq(lettertxt)
111 total2 = scale_freq(english_counts)
112 fit = value_diff(total2, total1)
113 if fit < best_fit:
114 best_key = shift
115 best_fit = fit
116 return best_key
117
118 def affine_break(message):
119 best_key = (0, 0, 0)
120 best_fit = float("inf")
121 for multiplier in range(1, 26, 2):
122 for shift in range(26):
123 for one_based in [True, False]:
124 plaintxt = affine_decipher_message(message, multiplier, shift, one_based)
125 lettertxt = letter_frequencies(plaintxt)
126 total1 = scale_freq(lettertxt)
127 total2 = scale_freq(english_counts)
128 fit = value_diff(total2, total1)
129 if fit < best_fit:
130 best_key = (multiplier, shift, one_based)
131 best_fit = fit
132 return best_key