This repository has been archived by the owner on Aug 14, 2020. It is now read-only.
forked from jameslyons/python_cryptanalysis
-
Notifications
You must be signed in to change notification settings - Fork 1
/
break_fracmorse.py
59 lines (52 loc) · 2.64 KB
/
break_fracmorse.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# usage: python break_fracmorse.py 'CIPHERTEXTMESSAGE'
# ideally you'll want 200 or so characters to reliably decrypt, shorter will often work but not as reliably.
import random
from ngram_score import ngram_score
import re
import sys
from pycipher import FracMorse
#ctext = FracMorse('PQRSTUVWXYZABCDEFGHIJKLMNO').encipher("He has not been returned to sea because of his affection for caregivers.The waitress pointed to the lunch menu, but the oldest living ex-major leaguer had no use for it")
fitness = ngram_score('fmorse_quadgrams.txt') # load our quadgram model
# helper function, converts an integer 0-25 into a character
def i2a(i): return 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[i%26]
# decipher a piece of text using the substitution cipher and a certain key
def sub_decipher(text,key):
invkey = [i2a(key.index(i)) for i in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ']
ret = ''
for c in text:
if c.isalpha(): ret += invkey[ord(c.upper())-ord('A')]
else: ret += c
return ret
# This code is just the simple substitution cipher cracking code, it works perfectly for fractionated morse as
# long as you use fractioned morse statistics instead of english statistics.
def break_simplesub(ctext,startkey=None):
''' perform hill-climbing with a single start. This function may have to be called many times
to break a substitution cipher. '''
# make sure ciphertext has all spacing/punc removed and is uppercase
ctext = re.sub('[^A-Z]','',ctext.upper())
parentkey,parentscore = startkey or list('ABCDEFGHIJKLMNOPQRSTUVWXYZ'),-99e99
if not startkey: random.shuffle(parentkey)
parentscore = fitness.score(sub_decipher(ctext,parentkey))
count = 0
while count < 1000:
a = random.randint(0,25)
b = random.randint(0,25)
child = parentkey[:]
# swap two characters in the child
child[a],child[b] = child[b],child[a]
score = fitness.score(sub_decipher(ctext,child))
# if the child was better, replace the parent with it
if score > parentscore:
parentscore, parentkey = score, child[:]
count = 0 # reset the counter
count += 1
return parentscore, parentkey
ctext = sys.argv[1]
ctext = re.sub(r'[^A-Z ]','',ctext.upper())
maxscore, maxkey = break_simplesub(ctext,list('ABCDEFGHIJKLMNOPQRSTUVWXYZ'))
print str(maxscore),'FractionatedMorse key:',''.join(maxkey), 'decrypt: ',FracMorse(maxkey).decipher(ctext)
for i in range(1000):
score, key = break_simplesub(ctext)
if score > maxscore:
maxscore,maxkey = score,key[:]
print str(maxscore),'FractionatedMorse key:',''.join(maxkey), 'decrypt: ',FracMorse(maxkey).decipher(ctext)