I'm going to assume this isn't homework because otherwise reading this answer would count as cheating. If it is homework please tell me so I can delete this answer.
Crib dragging does work, you just need to be more persistent. I wrote a script (available via GitHub) that answers your question:
#Use this tool to solve multi-time pads
#Written on 4th march 2016
#Took me a good 30 minutes
#How this tool works:
#Suppose you guess a crib " the "
#We have n messages each of length k
#For each of the n messages and at each position k we want to guess the crib
#However, we can make our lives vastly easier if we guess the other way round:
#Instead of guessing each message separately, we guess at the same position for all the messages at the same time
#This will make it easier to spot when crib for message m at position k produces the wrong output for any other message
#Therefore, what this program does is to guess for each combination of message and position the crib for all messages at position k
#Suppose at message m position k the plaintext is " the ", then we can derive the key at position k with the length of our crib
#Once we have derived that part of the key we then apply it to the rest of the messages to see if we get a sensible output
#Therefore for any crib there are n * k outputs where n is the number of ciphertexts and k is the length of each ciphertext
#Each output is n strings of characters representing the "decoded" portion of each message at position k
#Todo: Use a dictionary to automate this.
from itertools import combinations
crib = " the ".encode("hex")
#data = [line.strip() for line in open("20k.txt", 'r')]
binary_a = a.decode("hex")
binary_b = b.decode("hex")
binary_c = c.decode("hex")
binary_d = d.decode("hex")
binary_e = e.decode("hex")
binary_f = f.decode("hex")
def xor_strings(xs, ys,i=0):
return "".join(chr(ord(x) ^ ord(y)) for x, y in zip(xs[i:], ys))
'''for combo in combinations(L1, 2):
xored= xor_strings(combo, combo).encode("hex")
print combo.encode("hex"), combo.encode("hex")
for i in range(27):
for cipher in L1:
for position in range(len(cipher)-len(crib)):
key = xor_strings(cipher,crib.decode("hex"),position).encode("hex") #when we XOR our crib with the ciphertext, we should get back the key
for c in L1:
print xor_strings(c,key.decode("hex"),position), cipher.encode("hex"),position #we then XOR the key we got back with the other ciphertexts
key = xor_strings(ciphertext,crib.decode("hex"),position).encode("hex")
for c in L1:
print xor_strings(c,key.decode("hex"),position), c.encode("hex"),position #we then XOR the key we got back with the other ciphertexts
# Step 1. Do an initial crib drag through all possible key fragments for your crib.
# Step 2. Once you have found a valid key fragment, expand on that fragment. I did this manually in the interpreter.
crib_drag(" the ".encode("hex"),L1,5)
crib_drag("chinese puzzle ".encode("hex"),L1,0)
crib_drag("notable prisoner ".encode("hex"),L1,0)
crib_drag("do not disturb those ".encode("hex"),L1,0)
crib_drag("notable prisoner is freed ".encode("hex"),L1,0)
The difference between the method I used and the one posted elsewhere is that I drag the crib over all ciphertexts at the same time, whereas the one posted in the blog only compared a pair of ciphertexts at a time, making it much more prone to false positives compared to my method.
If you start with crib dragging using
the as the crib then you will find
When you assume that the 5th position of
73cc5457bf17e7a4750c5423b178d0a44fff756355c03cabc28a begins with
the. This stands out because almost all the other results you get will have weird symbols or obviously nonsensical combinations of letters whereas this one looks like a possible fragment of a grammatically correct English sentence. From there onwards you just keep lengthening the crib to get the rest of the key.