Last active
April 24, 2019 00:53
-
-
Save rgov/6d2ea6f324258674a97b664cba167681 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import random | |
# Here is our plaintext message | |
message = 'THISISATESTITISONLYATEST' | |
# Insert random spaces into the message | |
while len(message) < 72: | |
i = random.randrange(len(message)) | |
message = message[:i] + ' ' + message[i:] | |
# Write the message across the six wheels | |
wheels = [[' '] * 72 for _ in range(6)] | |
for i, m in enumerate(message): | |
if m == ' ': | |
for w in wheels: | |
w[i] = random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ') | |
else: | |
random.choice(wheels)[i] = m | |
# Generate a key | |
key = [ random.randrange(72) for _ in wheels ] | |
# Rotate the wheels the opposite direction | |
for w, k in zip(wheels, key): | |
w[:] = w[72 - k:] + w[:72 - k] | |
print('Scrambled wheels:') | |
for w in wheels: | |
print(' '.join(w)) | |
print() | |
########################### | |
# Now let's try to brute force how wheel[i] aligns to wheel[0] | |
found_key = [ 0 for _ in wheels ] | |
for i, w in enumerate(wheels[1:]): | |
scores = [] | |
for k in range(72): | |
collide = 0 | |
gap = 0 | |
letter = 0 | |
for a, b in zip(wheels[0], w[k:] + w[:k]): | |
if a == ' ' and b == ' ': | |
gap += 1 | |
elif a == ' ' or b == ' ': | |
letter += 1 | |
else: | |
collide += 1 | |
scores.append((k, collide, gap, letter)) | |
# Select the rotation with the lowest letter count | |
best_score = min(scores, key=lambda score: score[3]) | |
found_key[i + 1] = best_score[0] | |
# Rotate all the wheels according to our found key | |
for w, k in zip(wheels, found_key): | |
w[:] = w[k:] + w[:k] | |
print('Unscrambled wheels:') | |
for w in wheels: | |
print(' '.join(w)) | |
print() | |
# We can now combine wheels and drop out any colliding characters: | |
decoded = '' | |
for i, chars in enumerate(zip(*wheels)): | |
if sum(1 for x in chars if x != ' ') > 1: | |
decoded += ' ' | |
else: | |
decoded += next(x for x in chars if x != ' ') | |
# We don't know the orientation of the first wheel, but we can show all possible | |
# de-scramblings | |
print('Possible keys and plaintexts:') | |
for k in range(72): | |
out_key = [ (f + k) % 72 for f in found_key ] | |
print(out_key, ''.join(decoded[k:] + decoded[:k]).replace(' ', ''), end='') | |
print(' <--' if out_key == key else '') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment