#!/usr/bin/python3
import string
import itertools
import sys
import math
import time
import re
def reverse(s): return s[::-1]
def rotate(s): return s[1:] + s[0]
if len(sys.argv) > 1:
CIPHERTEXT = sys.argv[1]
else:
CIPHERTEXT="8BFGTY4PLU67-RTYO06.45:GN63-74PHGJI E67-:F563-21-574.9 ER34.6-DER8+WEST U.5 -RTG10 RTH8-4 6T.WR4564-21 +G134.2 RT55.4 GDW THE42.1LY 764.2Y- 45TG4.BTJ-Y.6ORT437.1D341-67.Y5DS 243 45TY-3234"
CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 +-.:"
# "Known" plaintext words/phrases in order of priority and
# increasing length...
#WORDS = [ "MUSCA", "DARK", "REGION" ]
WORDS = [ "MUSCA", "MUSCA DARK", "MUSCA DARK REGION", "PJ-P" ]
def test_match(word, map_from, map_to):
global Count
if Count % PRINT_EVERY == 0:
print("%9d %s --> %s" % (Count, map_from, map_to) )
Count += 1
plaintext = CIPHERTEXT.translate( str.maketrans(map_from, map_to) )
#print( "%2d: %s" % (i, plaintext) )
if word in plaintext:
print(map_from + ' -----> ' + map_to)
print(plaintext)
print("")
return True
else:
return False
def recurse_wordset( words, word, wordset, avail_chars, map_from, map_to ):
#print(">>>> recurse_wordset(-,%s,%s, %s, %s, %s)"%(
# word, wordset, avail_chars, map_from, map_to ))
if not wordset:
# Done recursing into wordset (all chars in 'word');
# Now see if the mapping matches 'word'
if test_match( word, map_from, map_to ):
# recurse into the next word...
recurse_words( words, avail_chars, map_from, map_to )
return
# pop the next char off wordset and add/iterate it into
# the mapping if it doesn't already exist.
while True:
char = wordset[0]
wordset = wordset[1:]
playable = char not in map_to
if playable or not wordset: break
if playable:
# iterate over all mappings from avail --> char
for c in avail_chars:
recurse_wordset(words, word, wordset,
avail_chars.replace(c, ''),
map_from + c,
map_to + char)
else:
# not playable; wordset must now be empty
recurse_wordset(words, word, wordset,
avail_chars, map_from, map_to)
def recurse_words( words, avail_chars, map_from, map_to):
# pop the next word and test it
if words:
words = words[:]
word = words.pop(0)
print("========= SEARCHING: %s ============" % word )
recurse_wordset( words, word, word, avail_chars, map_from, map_to )
else:
print("MATCHED ALL WORDS")
exit(0)
PRINT_EVERY = 100000
#PRINT_EVERY = 1
Count = 0
print( "CIPHERTEXT = %s" % CIPHERTEXT )
print( "CHARSET = %s" % CHARSET )
recurse_words( WORDS, CHARSET, "", "" )
# Here's the MUSCA DARK match:
#map_from = 'T.WR456-2'
#map_to = 'MUSCA DRK'
#charset = re.sub( "[T.WR4562]", '', CHARSET)
#charset = charset.replace('-', '')
#recurse_words( WORDS, charset, map_from, map_to)
print( "Count = " + str(Count))