#!/usr/bin/python3
import functools
import itertools
import logging
import math
import multiprocessing
import queue
import sys
import threading
import time
import string
q = queue.Queue()
def reverse(s): return s[::-1]
def rotate(s): return s[1:] + s[0]
PRINT_EVERY = 10000
COUNT = 0
BITS = ["ABCDEFGHIJKLM", "NOPQRSTUVWXYZ", "0123456789"] + list(" +-.:")
chunks = ['8BFGTY4PLU6', '7-RTYO06.45', ':GN63-74PHG', 'JI E67-:F56', '3-21-574.9 ',
'ER34.6-DER8', '+WEST U.5 -', 'RTG10 RTH8-', '4 6T.WR4564', '-21 +G134.2',
' RT55.4 GDW', ' THE42.1LY ', '764.2Y- 45T', 'G4.BTJ-Y.6O', 'RT437.1D341',
'-67.Y5DS 24', '3 45TY-3234']
CHARSET = string.ascii_uppercase + "0123456789" + " +-.:"
SENTINELS = ["LATTITUDE", "LONGTITUDE", "MUSCA", "CHUKCHAN", "COALSACK", "ADAMASTOR", "THARGOID", "AZIMUTH", "CARVER"]
def testmap(mapping, ciphertext):
plain = ciphertext.translate(str.maketrans(CHARSET, mapping))
for txt in SENTINELS:
if txt in plain:
logging.info("\n\n\nFOUND!\n\n\n\n")
loggin.info(mapping)
raise SystemExit
def permset(charset_bits, ciphertext):
# permute every part of charset_bits
global COUNT
for perm in itertools.permutations(charset_bits):
mapping = ''.join(perm)
if COUNT % PRINT_EVERY == 0:
# print("%9d %s" % (COUNT, mapping))
logging.info("%9d %s" % (COUNT, mapping))
testmap(mapping, ciphertext)
COUNT += 1
def rotate_reverse_bit(head, tail, ciphertext):
# print(">>>" + str(head) + str(tail))
if tail:
tail = tail[:] # copy
bit = tail.pop(0)
if len(bit) > 2:
for i in range(len(bit)):
rotate_reverse_bit(head + [bit], tail, ciphertext)
rotate_reverse_bit(head + [reverse(bit)], tail, ciphertext)
bit = rotate(bit)
elif len(bit) == 2:
rotate_reverse_bit(head + [bit], tail, ciphertext)
rotate_reverse_bit(head + [reverse(bit)], tail, ciphertext)
else:
rotate_reverse_bit(head + [bit], tail, ciphertext)
else: # end of recursion; do it
# print(head)
permset(head, ciphertext)
return
def count_perms(bits):
bcount = math.factorial(len(bits)) # base number of permutations
for bit in bits:
if len(bit) > 2:
bcount *= len(bit) # rotations
if len(bit) > 1:
bcount *= 2 # reversal
return bcount
def worker(head, tail):
while not q.empty():
ciphertext = q.get()
rotate_reverse_bit(head, tail, ciphertext)
q.task_done()
def split_every(n, iterable):
i = iter(iterable)
piece = list(itertools.islice(i, n))
while piece:
yield piece
piece = list(itertools.islice(i, n))
log_format = '[%(threadName)s] %(message)s'
logging.basicConfig(stream=sys.stdout, level=logging.INFO, format=log_format)
all_permutations = itertools.permutations(chunks)
number_of_cores = multiprocessing.cpu_count() * 2
for turn in split_every(number_of_cores, all_permutations):
threads = []
for _ in turn:
q.put("".join(_))
for _ in range(number_of_cores):
worker_thread = threading.Thread(target=worker, args=[[], BITS])
worker_thread.daemon = True
worker_thread.start()
threads.append(worker_thread)
while len(threading.enumerate()) > 1:
time.sleep(10)