1
1
LETTER_FREQUENCIES_DICT = {
2
- 'A' : 8.12 , 'B' : 1.49 , 'C' : 2.71 , 'D' : 4.32 , 'E' : 12.02 , 'F' : 2.3 , 'G' : 2.03 ,
3
- 'H' : 5.92 , 'I' : 7.31 , 'J' : 0.1 , 'K' : 0.69 , 'L' : 3.92 , 'M' : 2.61 ,
4
- 'N' : 6.95 , 'O' : 7.68 , 'P' : 1.82 , 'Q' : 0.11 , 'R' : 6.02 , 'S' : 6.28 ,
5
- 'T' : 9.10 , 'U' : 2.88 , 'V' : 1.11 , 'W' : 2.09 , 'X' : 0.17 , 'Y' : 2.11 , 'Z' : 0.07
2
+ "A" : 8.12 ,
3
+ "B" : 1.49 ,
4
+ "C" : 2.71 ,
5
+ "D" : 4.32 ,
6
+ "E" : 12.02 ,
7
+ "F" : 2.3 ,
8
+ "G" : 2.03 ,
9
+ "H" : 5.92 ,
10
+ "I" : 7.31 ,
11
+ "J" : 0.1 ,
12
+ "K" : 0.69 ,
13
+ "L" : 3.92 ,
14
+ "M" : 2.61 ,
15
+ "N" : 6.95 ,
16
+ "O" : 7.68 ,
17
+ "P" : 1.82 ,
18
+ "Q" : 0.11 ,
19
+ "R" : 6.02 ,
20
+ "S" : 6.28 ,
21
+ "T" : 9.10 ,
22
+ "U" : 2.88 ,
23
+ "V" : 1.11 ,
24
+ "W" : 2.09 ,
25
+ "X" : 0.17 ,
26
+ "Y" : 2.11 ,
27
+ "Z" : 0.07 ,
6
28
}
7
29
LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
8
30
PARAMETER = 0.0665 # index of confidence of the entire language (for english 0.0665)
9
- MAX_KEYLENGTH = None # None is the default, you can also try a positive integer (example: 10)
31
+ MAX_KEYLENGTH = (
32
+ None # None is the default, you can also try a positive integer (example: 10)
33
+ )
10
34
11
35
12
36
def index_of_coincidence (frequencies : dict , length : int ) -> float :
@@ -20,8 +44,8 @@ def index_of_coincidence(frequencies: dict, length: int) -> float:
20
44
"""
21
45
index = 0.0
22
46
for value in frequencies .values ():
23
- index += value * (value - 1 )
24
- return index / (length * (length - 1 ))
47
+ index += value * (value - 1 )
48
+ return index / (length * (length - 1 ))
25
49
26
50
27
51
def calculate_indexes_of_coincidence (ciphertext : str , step : int ) -> list :
@@ -38,7 +62,7 @@ def calculate_indexes_of_coincidence(ciphertext: str, step: int) -> list:
38
62
39
63
# for every starting point in [0, step)
40
64
for j in range (step ):
41
- frequencies = dict ()
65
+ frequencies = dict [ str , int ]
42
66
c = 0
43
67
for i in range (0 + j , length , step ):
44
68
c += 1
@@ -52,7 +76,7 @@ def calculate_indexes_of_coincidence(ciphertext: str, step: int) -> list:
52
76
return indexes_of_coincidence
53
77
54
78
55
- def friedman_method (ciphertext : str , max_keylength : int = None ) -> int :
79
+ def friedman_method (ciphertext : str , max_keylength : int | None = None ) -> int :
56
80
"""
57
81
Implements Friedman's method for finding the length of the key of a Vigenere cipher. It finds the length with an
58
82
index of confidence closer to that of an average text in the english language. Check the wikipedia page:
@@ -67,11 +91,12 @@ def friedman_method(ciphertext: str, max_keylength: int=None) -> int:
67
91
if max_keylength is None :
68
92
max_keylength = len (ciphertext )
69
93
70
- frequencies = [1.5 ] # the zeroth position should not be used: length of key is greater than zero
94
+ frequencies = [
95
+ 1.5
96
+ ] # the zeroth position should not be used: length of key is greater than zero
71
97
72
98
# for every length of key
73
99
for i in range (1 , max_keylength + 1 ):
74
-
75
100
# for a specific length it finds the minimum index of coincidence
76
101
min1 = 15.0
77
102
for val in calculate_indexes_of_coincidence (ciphertext , i ):
@@ -90,7 +115,7 @@ def friedman_method(ciphertext: str, max_keylength: int=None) -> int:
90
115
91
116
def get_frequencies () -> tuple :
92
117
"""Return the values of the global variable @LETTER_FREQUENCIES_DICT as a tuple ex. (0.25, 1.42, ...)."""
93
- t = tuple (LETTER_FREQUENCIES_DICT [chr (i )] for i in range (ord ('A' ), ord ('A' ) + 26 ))
118
+ t = tuple (LETTER_FREQUENCIES_DICT [chr (i )] for i in range (ord ("A" ), ord ("A" ) + 26 ))
94
119
return tuple (num / 100 for num in t )
95
120
96
121
@@ -107,7 +132,7 @@ def find_key(ciphertext: str, key_length: int) -> str:
107
132
:param key_length: a supposed length of the key
108
133
:return: the key as a string
109
134
"""
110
- a = ord ('A' )
135
+ a = ord ("A" )
111
136
cipher_length = len (ciphertext )
112
137
alphabet_length = 26 # the length of the english alphabet
113
138
@@ -117,7 +142,7 @@ def find_key(ciphertext: str, key_length: int) -> str:
117
142
for k in range (key_length ):
118
143
# find the frequencies of the letters in the message:
119
144
# the frequency of 'A' is in the first position of the freq list and so on
120
- freq = [0 ] * alphabet_length
145
+ freq = [0.0 ] * alphabet_length
121
146
c = 0
122
147
for i in range (k , cipher_length , key_length ):
123
148
freq [ord (ciphertext [i ]) - a ] += 1
@@ -131,7 +156,9 @@ def find_key(ciphertext: str, key_length: int) -> str:
131
156
new_val = sum ((freq [j ] * real_freq [j ]) for j in range (alphabet_length ))
132
157
if max1 [0 ] < new_val :
133
158
max1 = [new_val , i ]
134
- freq .append (freq .pop (0 )) # shift the list cyclically one position to the left
159
+ freq .append (
160
+ freq .pop (0 )
161
+ ) # shift the list cyclically one position to the left
135
162
key .append (max1 [1 ])
136
163
137
164
return "" .join (chr (num + a ) for num in key ) # return the key as a string
@@ -142,12 +169,12 @@ def find_key_from_vigenere_cipher(ciphertext: str) -> str:
142
169
Tries to find the key length and then the actual key of a Vigenere ciphertext. It uses Friedman's method and
143
170
statistical analysis. It works best for large pieces of text written in the english language.
144
171
"""
145
- clean_ciphertext = list ()
172
+ clean_ciphertext_list = list ()
146
173
for symbol in ciphertext .upper ():
147
174
if symbol in LETTERS :
148
- clean_ciphertext .append (symbol )
175
+ clean_ciphertext_list .append (symbol )
149
176
150
- clean_ciphertext = "" .join (clean_ciphertext )
177
+ clean_ciphertext = "" .join (clean_ciphertext_list )
151
178
152
179
key_length = friedman_method (clean_ciphertext , max_keylength = MAX_KEYLENGTH )
153
180
print (f"The length of the key is { key_length } " )
@@ -158,7 +185,7 @@ def find_key_from_vigenere_cipher(ciphertext: str) -> str:
158
185
return key
159
186
160
187
161
- if __name__ == ' __main__' :
188
+ if __name__ == " __main__" :
162
189
c = ""
163
190
k = find_key_from_vigenere_cipher (c )
164
191
print (k )
0 commit comments