-
Notifications
You must be signed in to change notification settings - Fork 0
/
Scytale.py
132 lines (100 loc) · 4.3 KB
/
Scytale.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# Description: This file contains the functions to decode a message encoded with a Scytale algorithm with an unknown key
import matplotlib.pyplot as plt
from common import open_file, save
import logging
common_word_list = ["le", "de", "un", "être", "et", "à", "il", "avoir", "ne", "je", "son", "que", "se", "qui", "ce", "dans", "en", "du", "elle", "au", "bonjour","Joël","--"]
def find_most_likely(possible_width):
"""
Finds the most likely key from a list of possible keys.
Args:
possible_width (list): A list of tuples containing possible widths and their corresponding scores.
Returns:
int: The most likely width.
"""
most_likely = max(possible_width, key=lambda x: x[1])[0]
return most_likely
def number_of_words(message):
"""
Counts the number of common words in a given message.
Parameters:
message (str): The input message to count the words from.
Returns:
int: The number of common words in the message.
"""
word_count = 0
List_Word = message.split()
for word in List_Word:
if word in common_word_list:
word_count += 1
return word_count
def brutforce_Scytale(message, max_key):
"""
Brute force method to decrypt a Scytale cipher. It tests all possible key sizes and returns a list of possible keys and their corresponding word count.
Args:
message (str): The encrypted message.
max_key (int, optional): The maximum key size to test.
Returns:
list: A list of tuples containing possible keys and the corresponding word count.
"""
current_key = 1
possible_key = []
if max_key > len(message): # if the maximum width is greater than the length of the message, reduce it
max_key = len(message)
for current_key in range(max_key): # test all possible widths
word_count = 0
newmessage = decode_Scytale(message, current_key)
word_count = number_of_words(newmessage)
if word_count != 0:
possible_key.append((current_key, word_count))
logging.debug("Key= " +str(current_key)+ " Word count= "+ str(word_count))
return possible_key
def decode_Scytale(message, key):
"""
Decode a message encrypted using the Scytale cipher.
Args:
message (str): The encrypted message.
key (int): The key used for encryption.
Returns:
str: The decoded message.
"""
newmessage = ""
for loop in range(key):
newmessage += message[loop::key]
return newmessage
def Scytale(message, max_key=500):
"""
Decode a message encrypted using the Scytale cipher with an unknown key.
Args:
message (str): The message to be decoded.
key (int, optional): The key used for the Scytale cipher. Defaults to 500.
Returns:
tuple: A tuple containing the decoded message and the key used for decoding.
Raises:
ValueError: If no word is found in the all possible decoded message. Try increasing the max_key, changing the algorithm, or increasing the size of the dictionary.
"""
possible_key = brutforce_Scytale(message, max_key)
if possible_key: #check if possible_key is not empty
most_likely = find_most_likely(possible_key)
return decode_Scytale(message, most_likely), most_likely
else:
raise ValueError("No word found in all possible decoded message. Try to increase max_key, changing algorithm, or increasing the size of the dictionary")
if __name__ == "__main__":
# decode the message and save it
logging.basicConfig(level=logging.INFO, format='%(levelname)s-%(asctime)s : %(message)s', datefmt='%H:%M:%S')
path=r'.\Messages\Encoded_messages\message_1.txt'
message = open_file(path)
decoded_message, key = Scytale(message)
logging.info(f"{decoded_message[:100]} \n[...]\n {decoded_message[-100:]} \n")
logging.info(f"Key= {key}")
save (decoded_message)
# def plot(possible_key):
# """
# Plot the possible key values. It is non-blocking, so the program will continue to run after the plot is shown.
# Parameters:
# possible_key (list): A list of tuples containing the key and count values.
# Returns:
# None
# """
# for key, count in possible_key:
# plt.plot(key, count, 'r.')
# plt.show(block=False)