from math import log2 ALPHABET = "\n !\"'(),-.0123456789:;?ABCDEFGHIJKLMNOPQRSTUVWXYZ" def encrypt(text, key): text = text.upper() key = key.upper() encrypted = "" for position in range(len(text)): text_character = ALPHABET.index(text[position]) key_character = ALPHABET.index(key[position % len(key)]) encrypted_char = (text_character + key_character) % len(ALPHABET) encrypted += ALPHABET[encrypted_char] return encrypted def get_frequencies(text): text = text.upper() freq = 1 / len(text) frequencies = {} for char in text: if frequencies.get(char): frequencies[char] += freq else: frequencies[char] = freq return frequencies def cross_entropy(freqs1, freqs2): freqs1_chars = set(freqs1.keys()) freqs2_chars = set(freqs2.keys()) all_chars_union = list(set.union(freqs1_chars, freqs2_chars)) total = 0.0 min_freq1 = 1 for value in freqs1.values(): if min_freq1 > value: min_freq1 = value min_freq2 = 1 for value in freqs2.values(): if min_freq2 > value: min_freq2 = value for char in all_chars_union: char_in_freqs1 = freqs1.get(char) char_in_freqs2 = freqs2.get(char) if char_in_freqs1 and not char_in_freqs2: freqs2[char] = min_freq2 elif not char_in_freqs1 and char_in_freqs2: freqs1[char] = min_freq1 total -= freqs1[char] * log2(freqs2[char]) return total def guess_key(encrypted): q = '' with open('frank.txt') as f: q = f.read() english_frequencies = get_frequencies(q) cipher = ['', '', ''] key = ['', '', ''] i = 0 for char in encrypted: cipher[i % 3] += char i += 1 for index in range(len(cipher)): min_val = 99999 for char in ALPHABET: val = cross_entropy(english_frequencies, get_frequencies(decrypt(cipher[index], char))) if val > 0 and val < min_val: min_val = val key[index] = char return ''.join(key) def crack(encrypted_text): key = guess_key(encrypted_text) return decrypt(encrypted_text, key) def decrypt(text, key): text = text.upper() key = key.upper() decrypted = "" for position in range(len(text)): text_character = ALPHABET.index(text[position]) key_character = ALPHABET.index(key[position % len(key)]) decrypted_char = (text_character - key_character) % len(ALPHABET) decrypted += ALPHABET[decrypted_char] return decrypted if __name__ == '__main__': encrypted = '' with open('secret1_encrypted.txt') as f: encrypted = f.read() print(crack(encrypted[:1000]))