Skip to content

Commit e120ae4

Browse files
committed
made friedman_method
1 parent cc0416c commit e120ae4

File tree

1 file changed

+38
-4
lines changed

1 file changed

+38
-4
lines changed

ciphers/break_vigenere.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
'N': 6.95, 'O': 7.68, 'P': 1.82, 'Q': 0.11, 'R': 6.02, 'S': 6.28,
55
'T': 9.10, 'U': 2.88, 'V': 1.11, 'W': 2.09, 'X': 0.17, 'Y': 2.11, 'Z': 0.07
66
}
7-
87
LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
8+
PARAMETER = 0.0665 # index of confidence of the entire language (for english 0.0665)
99

1010

1111
def index_of_coincidence(frequencies: dict, length: int) -> float:
@@ -17,7 +17,7 @@ def index_of_coincidence(frequencies: dict, length: int) -> float:
1717
"""
1818
index = 0.0
1919
for value in frequencies.values():
20-
index += (value/length)**2
20+
index += (value / length) ** 2
2121
return index
2222

2323

@@ -37,7 +37,7 @@ def calculate_indexes_of_coincidence(ciphertext: str, step: int) -> list:
3737
for j in range(step):
3838
frequencies = dict()
3939
c = 0
40-
for i in range(0+j, length, step):
40+
for i in range(0 + j, length, step):
4141
c += 1
4242
try: # in case the frequencies dictionary does not already have this key
4343
frequencies[ciphertext[i]] += 1
@@ -48,6 +48,40 @@ def calculate_indexes_of_coincidence(ciphertext: str, step: int) -> list:
4848
return indexes_of_coincidence
4949

5050

51+
def friedman_method(ciphertext: str, max_keylength: int=None) -> int:
52+
"""
53+
Implements Friedman's method for finding the length of the key of a Vigenere cipher. It finds the length with an
54+
index of confidence closer to that of an average text in the english language.
55+
:param ciphertext: a string (text)
56+
:param max_keylength: the maximum length of key that Friedman's method should check, if None then it defaults to the
57+
length of the cipher
58+
:return: the length of the key
59+
"""
60+
# sets the default value of MAX_KEYLEBGTH
61+
if max_keylength is None:
62+
max_keylength = len(ciphertext)
63+
64+
frequencies = [1.5] # the zeroth position should not be used: length of key is greater than zero
65+
66+
# for every length of key
67+
for i in range(1, max_keylength + 1):
68+
69+
# for a specific length it finds the minimum index of coincidence
70+
min1 = 15.0
71+
for val in calculate_indexes_of_coincidence(ciphertext, i):
72+
if abs(val - PARAMETER) < abs(min1 - PARAMETER):
73+
min1 = val
74+
frequencies.append(min1)
75+
76+
# finds which length of key has the minimum difference with the language PARAMETER
77+
li = (15.0, -1) # initialization
78+
for i in range(len(frequencies)):
79+
if abs(frequencies[i] - PARAMETER) < abs(li[0] - PARAMETER):
80+
li = (frequencies[i], i)
81+
82+
return li[1]
83+
84+
5185
def find_key_from_vigenere_cipher(ciphertext: str) -> str:
5286
clean_ciphertext = list()
5387
for symbol in ciphertext:
@@ -61,4 +95,4 @@ def find_key_from_vigenere_cipher(ciphertext: str) -> str:
6195

6296

6397
if __name__ == '__main__':
64-
print(index_of_coincidence(LETTER_FREQUENCIES_DICT, 1000))
98+
print(index_of_coincidence(LETTER_FREQUENCIES_DICT, 1000))

0 commit comments

Comments
 (0)