52 lines
1.3 KiB
Python
52 lines
1.3 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import itertools
|
|
import requests
|
|
import string
|
|
|
|
URL = "https://projecteuler.net/project/resources/p059_cipher.txt"
|
|
|
|
def get_ciphertext(url):
|
|
resp = requests.get(url)
|
|
return resp.text
|
|
|
|
|
|
def generate_keys(letters, length):
|
|
perms = itertools.permutations(letters, length)
|
|
yield from perms
|
|
|
|
|
|
def ints_to_string(numbers):
|
|
return [chr(num) for num in numbers]
|
|
|
|
|
|
def string_to_ints(letters):
|
|
return [ord(letter) for letter in letters]
|
|
|
|
|
|
def xor_lists(xs, ys):
|
|
if len(xs) != len(ys):
|
|
raise Exception("Lists must be the same length")
|
|
|
|
return [a ^ b for a, b in zip(xs, ys)]
|
|
|
|
|
|
def main():
|
|
text = get_ciphertext(URL)
|
|
cipher = [int(num) for num in text.split(',')]
|
|
letters = string.ascii_lowercase
|
|
common_words = ["the", "of", "to", "and", "in", "is", "it", "that"]
|
|
for perm in generate_keys(letters, 3):
|
|
key = "".join(list(perm))
|
|
cycled = string_to_ints(list(key * (len(cipher) // len(key))))
|
|
decrypt = "".join(ints_to_string(xor_lists(cipher, cycled)))
|
|
if all(word in decrypt for word in common_words):
|
|
print("Decrypted passage:")
|
|
print(decrypt)
|
|
print("Key: {0}".format(key))
|
|
print("Sum: {0}".format(sum(xor_lists(cipher, cycled))))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|