X-Git-Url: https://git.njae.me.uk/?a=blobdiff_plain;f=break.py;h=7c27216daf0be2d680168302ecb1f379b65c8fdd;hb=84f8833b49b5fc8d1195e831753e01b203552e3e;hp=b97c37dcd27085e70562407560966881064c0b1a;hpb=754fda80881d7b4b7ee0d86f8bcc1cf02f83a253;p=cipher-tools.git diff --git a/break.py b/break.py index b97c37d..7c27216 100644 --- a/break.py +++ b/break.py @@ -3,7 +3,7 @@ import collections import norms import logging from itertools import zip_longest, cycle -from segment import segment +from segment import segment, Pwords from multiprocessing import Pool from cipher import * @@ -89,13 +89,13 @@ def caesar_break(message, >>> caesar_break('ibxcsyorsaqcheyklxivoexlevmrimwxsfiqevvmihrsasrxliwyrh' \ 'ecjsppsamrkwleppfmergefifvmhixscsymjcsyqeoixlm') # doctest: +ELLIPSIS - (4, 0.31863952890183...) + (4, 0.080345432737...) >>> caesar_break('wxwmaxdgheetgwuxztgptedbgznitgwwhpguxyhkxbmhvvtlbhgtee' \ 'raxlmhiixweblmxgxwmhmaxybkbgztgwztsxwbgmxgmert') # doctest: +ELLIPSIS - (19, 0.42152901235832...) + (19, 0.11189290326...) >>> caesar_break('yltbbqnqnzvguvaxurorgenafsbezqvagbnornfgsbevpnaabjurer' \ 'svaquvzyvxrnznazlybequrvfohgriraabjtbaruraprur') # doctest: +ELLIPSIS - (13, 0.316029208075451...) + (13, 0.08293968842...) """ sanitised_message = sanitise(message) best_shift = 0 @@ -126,7 +126,7 @@ def affine_break(message, 'ls umfjsd jlsi zg hfsqysxog. ls dmmdtsd mx jls bats mh bkbsf. ls ' \ 'bfmctsd kfmyxd jls lyj, mztanamyu xmc jm clm cku tmmeaxw kj lai kxd ' \ 'clm ckuxj.') # doctest: +ELLIPSIS - ((15, 22, True), 0.23570361818655...) + ((15, 22, True), 0.0598745365924...) """ sanitised_message = sanitise(message) best_multiplier = 0 @@ -167,7 +167,7 @@ def keyword_break(message, >>> keyword_break(keyword_encipher('this is a test message for the ' \ 'keyword decipherment', 'elephant', 1), \ wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS - (('elephant', 1), 0.41643991598441...) + (('elephant', 1), 0.1066453448861...) """ best_keyword = '' best_wrap_alphabet = True @@ -204,7 +204,7 @@ def keyword_break_mp(message, >>> keyword_break_mp(keyword_encipher('this is a test message for the ' \ 'keyword decipherment', 'elephant', 1), \ wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS - (('elephant', 1), 0.41643991598441...) + (('elephant', 1), 0.106645344886...) """ with Pool() as pool: helper_args = [(message, word, wrap, metric, target_counts, @@ -234,7 +234,7 @@ def scytale_break(message, >>> scytale_break('tfeulchtrtteehwahsdehneoifeayfsondmwpltmaoalhikotoere' \ 'dcweatehiplwxsnhooacgorrcrcraotohsgullasenylrendaianeplscdriioto' \ 'aek') # doctest: +ELLIPSIS - (6, 0.83453041115025...) + (6, 0.092599933059...) """ best_key = 0 best_fit = float("inf") @@ -265,22 +265,30 @@ def column_transposition_break(message, n-gram frequency analysis >>> column_transposition_break(column_transposition_encipher(sanitise( \ - "Turing's homosexuality resulted in a criminal prosecution in 1952, \ - when homosexual acts were still illegal in the United Kingdom. "), \ + "It is a truth universally acknowledged, that a single man in \ + possession of a good fortune, must be in want of a wife. However \ + little known the feelings or views of such a man may be on his \ + first entering a neighbourhood, this truth is so well fixed in the \ + minds of the surrounding families, that he is considered the \ + rightful property of some one or other of their daughters."), \ 'encipher'), \ translist={(2, 0, 5, 3, 1, 4, 6): ['encipher'], \ (5, 0, 6, 1, 3, 4, 2): ['fourteen'], \ (6, 1, 0, 4, 5, 3, 2): ['keyword']}) # doctest: +ELLIPSIS - ((2, 0, 5, 3, 1, 4, 6), 0.898128626285...) + ((2, 0, 5, 3, 1, 4, 6), 0.0628106372...) >>> column_transposition_break(column_transposition_encipher(sanitise( \ - "Turing's homosexuality resulted in a criminal prosecution in 1952, " \ - "when homosexual acts were still illegal in the United Kingdom."), \ + "It is a truth universally acknowledged, that a single man in \ + possession of a good fortune, must be in want of a wife. However \ + little known the feelings or views of such a man may be on his \ + first entering a neighbourhood, this truth is so well fixed in the \ + minds of the surrounding families, that he is considered the \ + rightful property of some one or other of their daughters."), \ 'encipher'), \ translist={(2, 0, 5, 3, 1, 4, 6): ['encipher'], \ (5, 0, 6, 1, 3, 4, 2): ['fourteen'], \ (6, 1, 0, 4, 5, 3, 2): ['keyword']}, \ target_counts=normalised_english_trigram_counts) # doctest: +ELLIPSIS - ((2, 0, 5, 3, 1, 4, 6), 1.1958792913127...) + ((2, 0, 5, 3, 1, 4, 6), 0.0592259560...) """ best_transposition = '' best_fit = float("inf") @@ -317,22 +325,30 @@ def column_transposition_break_mp(message, n-gram frequency analysis >>> column_transposition_break_mp(column_transposition_encipher(sanitise( \ - "Turing's homosexuality resulted in a criminal prosecution in 1952, \ - when homosexual acts were still illegal in the United Kingdom. "), \ + "It is a truth universally acknowledged, that a single man in \ + possession of a good fortune, must be in want of a wife. However \ + little known the feelings or views of such a man may be on his \ + first entering a neighbourhood, this truth is so well fixed in the \ + minds of the surrounding families, that he is considered the \ + rightful property of some one or other of their daughters."), \ 'encipher'), \ translist={(2, 0, 5, 3, 1, 4, 6): ['encipher'], \ (5, 0, 6, 1, 3, 4, 2): ['fourteen'], \ (6, 1, 0, 4, 5, 3, 2): ['keyword']}) # doctest: +ELLIPSIS - ((2, 0, 5, 3, 1, 4, 6), 0.898128626285...) + ((2, 0, 5, 3, 1, 4, 6), 0.0628106372...) >>> column_transposition_break_mp(column_transposition_encipher(sanitise( \ - "Turing's homosexuality resulted in a criminal prosecution in 1952, " \ - "when homosexual acts were still illegal in the United Kingdom."), \ + "It is a truth universally acknowledged, that a single man in \ + possession of a good fortune, must be in want of a wife. However \ + little known the feelings or views of such a man may be on his \ + first entering a neighbourhood, this truth is so well fixed in the \ + minds of the surrounding families, that he is considered the \ + rightful property of some one or other of their daughters."), \ 'encipher'), \ translist={(2, 0, 5, 3, 1, 4, 6): ['encipher'], \ (5, 0, 6, 1, 3, 4, 2): ['fourteen'], \ (6, 1, 0, 4, 5, 3, 2): ['keyword']}, \ target_counts=normalised_english_trigram_counts) # doctest: +ELLIPSIS - ((2, 0, 5, 3, 1, 4, 6), 1.1958792913127...) + ((2, 0, 5, 3, 1, 4, 6), 0.0592259560...) """ ngram_length = len(next(iter(target_counts.keys()))) with Pool() as pool: @@ -367,7 +383,7 @@ def vigenere_keyword_break(message, >>> vigenere_keyword_break(vigenere_encipher(sanitise('this is a test ' \ 'message for the vigenere decipherment'), 'cat'), \ wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS - ('cat', 0.4950195952826...) + ('cat', 0.15965224935...) """ best_keyword = '' best_fit = float("inf") @@ -400,7 +416,7 @@ def vigenere_keyword_break_mp(message, >>> vigenere_keyword_break_mp(vigenere_encipher(sanitise('this is a test ' \ 'message for the vigenere decipherment'), 'cat'), \ wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS - ('cat', 0.4950195952826...) + ('cat', 0.159652249358...) """ with Pool() as pool: helper_args = [(message, word, metric, target_counts, @@ -422,6 +438,41 @@ def vigenere_keyword_break_worker(message, keyword, metric, target_counts, return keyword, fit + +def vigenere_frequency_break(message, + metric=norms.euclidean_distance, + target_counts=normalised_english_counts, + message_frequency_scaling=norms.normalise): + """Breaks a Vigenere cipher with frequency analysis + + >>> vigenere_frequency_break(vigenere_encipher(sanitise("It is time to " \ + "run. She is ready and so am I. I stole Daniel's pocketbook this " \ + "afternoon when he left his jacket hanging on the easel in the " \ + "attic."), 'florence')) # doctest: +ELLIPSIS + ('florence', 0.077657073...) + """ + best_fit = float("inf") + best_key = '' + sanitised_message = sanitise(message) + for trial_length in range(1, 20): + splits = every_nth(sanitised_message, trial_length) + key = ''.join([chr(caesar_break(s, target_counts=target_counts)[0] + ord('a')) for s in splits]) + plaintext = vigenere_decipher(sanitised_message, key) + counts = message_frequency_scaling(frequencies(plaintext)) + fit = metric(target_counts, counts) + logger.debug('Vigenere key length of {0} ({1}) gives fit of {2}'. + format(trial_length, key, fit)) + if fit < best_fit: + best_fit = fit + best_key = key + logger.info('Vigenere break best fit with key {0} gives fit ' + 'of {1} and decrypt starting: {2}'.format(best_key, + best_fit, sanitise( + vigenere_decipher(message, best_key))[:50])) + return best_key, best_fit + + + if __name__ == "__main__": import doctest doctest.testmod()