X-Git-Url: https://git.njae.me.uk/?a=blobdiff_plain;f=amsco.py;fp=amsco.py;h=0000000000000000000000000000000000000000;hb=311b300d197536622980f7a837294d8245e326b4;hp=4eeeb5961cde67a55a6b28229dfccd729a8d9bcb;hpb=d7224fba67d9f99c01bd78ef669c96189686e4c2;p=cipher-tools.git diff --git a/amsco.py b/amsco.py deleted file mode 100644 index 4eeeb59..0000000 --- a/amsco.py +++ /dev/null @@ -1,204 +0,0 @@ -from enum import Enum -import multiprocessing -import itertools - -from utilities import * -from language_models import * -from column_transposition import transpositions - -from logger import logger - -# Where each piece of text ends up in the AMSCO transpositon cipher. -# 'index' shows where the slice appears in the plaintext, with the slice -# from 'start' to 'end' -AmscoSlice = collections.namedtuple('AmscoSlice', ['index', 'start', 'end']) - -class AmscoFillStyle(Enum): - continuous = 1 - same_each_row = 2 - reverse_each_row = 3 - -def amsco_transposition_positions(message, keyword, - fillpattern=(1, 2), - fillstyle=AmscoFillStyle.continuous, - fillcolumnwise=False, - emptycolumnwise=True): - """Creates the grid for the AMSCO transposition cipher. Each element in the - grid shows the index of that slice and the start and end positions of the - plaintext that go to make it up. - - >>> amsco_transposition_positions(string.ascii_lowercase, 'freddy', \ - fillpattern=(1, 2)) # doctest: +NORMALIZE_WHITESPACE - [[AmscoSlice(index=3, start=4, end=6), - AmscoSlice(index=2, start=3, end=4), - AmscoSlice(index=0, start=0, end=1), - AmscoSlice(index=1, start=1, end=3), - AmscoSlice(index=4, start=6, end=7)], - [AmscoSlice(index=8, start=12, end=13), - AmscoSlice(index=7, start=10, end=12), - AmscoSlice(index=5, start=7, end=9), - AmscoSlice(index=6, start=9, end=10), - AmscoSlice(index=9, start=13, end=15)], - [AmscoSlice(index=13, start=19, end=21), - AmscoSlice(index=12, start=18, end=19), - AmscoSlice(index=10, start=15, end=16), - AmscoSlice(index=11, start=16, end=18), - AmscoSlice(index=14, start=21, end=22)], - [AmscoSlice(index=18, start=27, end=28), - AmscoSlice(index=17, start=25, end=27), - AmscoSlice(index=15, start=22, end=24), - AmscoSlice(index=16, start=24, end=25), - AmscoSlice(index=19, start=28, end=30)]] - """ - transpositions = transpositions_of(keyword) - fill_iterator = itertools.cycle(fillpattern) - indices = itertools.count() - message_length = len(message) - - current_position = 0 - grid = [] - current_fillpattern = fillpattern - while current_position < message_length: - row = [] - if fillstyle == AmscoFillStyle.same_each_row: - fill_iterator = itertools.cycle(fillpattern) - if fillstyle == AmscoFillStyle.reverse_each_row: - fill_iterator = itertools.cycle(current_fillpattern) - for _ in range(len(transpositions)): - index = next(indices) - gap = next(fill_iterator) - row += [AmscoSlice(index, current_position, current_position + gap)] - current_position += gap - grid += [row] - if fillstyle == AmscoFillStyle.reverse_each_row: - current_fillpattern = list(reversed(current_fillpattern)) - return [transpose(r, transpositions) for r in grid] - -def amsco_transposition_encipher(message, keyword, - fillpattern=(1,2), fillstyle=AmscoFillStyle.reverse_each_row): - """AMSCO transposition encipher. - - >>> amsco_transposition_encipher('hellothere', 'abc', fillpattern=(1, 2)) - 'hoteelhler' - >>> amsco_transposition_encipher('hellothere', 'abc', fillpattern=(2, 1)) - 'hetelhelor' - >>> amsco_transposition_encipher('hellothere', 'acb', fillpattern=(1, 2)) - 'hotelerelh' - >>> amsco_transposition_encipher('hellothere', 'acb', fillpattern=(2, 1)) - 'hetelorlhe' - >>> amsco_transposition_encipher('hereissometexttoencipher', 'encode') - 'etecstthhomoerereenisxip' - >>> amsco_transposition_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 2)) - 'hetcsoeisterereipexthomn' - >>> amsco_transposition_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 2), fillstyle=AmscoFillStyle.continuous) - 'hecsoisttererteipexhomen' - >>> amsco_transposition_encipher('hereissometexttoencipher', 'cipher', fillpattern=(2, 1)) - 'heecisoosttrrtepeixhemen' - >>> amsco_transposition_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 3, 2)) - 'hxtomephescieretoeisnter' - >>> amsco_transposition_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 3, 2), fillstyle=AmscoFillStyle.continuous) - 'hxomeiphscerettoisenteer' - """ - grid = amsco_transposition_positions(message, keyword, - fillpattern=fillpattern, fillstyle=fillstyle) - ct_as_grid = [[message[s.start:s.end] for s in r] for r in grid] - return combine_every_nth(ct_as_grid) - - -def amsco_transposition_decipher(message, keyword, - fillpattern=(1,2), fillstyle=AmscoFillStyle.reverse_each_row): - """AMSCO transposition decipher - - >>> amsco_transposition_decipher('hoteelhler', 'abc', fillpattern=(1, 2)) - 'hellothere' - >>> amsco_transposition_decipher('hetelhelor', 'abc', fillpattern=(2, 1)) - 'hellothere' - >>> amsco_transposition_decipher('hotelerelh', 'acb', fillpattern=(1, 2)) - 'hellothere' - >>> amsco_transposition_decipher('hetelorlhe', 'acb', fillpattern=(2, 1)) - 'hellothere' - >>> amsco_transposition_decipher('etecstthhomoerereenisxip', 'encode') - 'hereissometexttoencipher' - >>> amsco_transposition_decipher('hetcsoeisterereipexthomn', 'cipher', fillpattern=(1, 2)) - 'hereissometexttoencipher' - >>> amsco_transposition_decipher('hecsoisttererteipexhomen', 'cipher', fillpattern=(1, 2), fillstyle=AmscoFillStyle.continuous) - 'hereissometexttoencipher' - >>> amsco_transposition_decipher('heecisoosttrrtepeixhemen', 'cipher', fillpattern=(2, 1)) - 'hereissometexttoencipher' - >>> amsco_transposition_decipher('hxtomephescieretoeisnter', 'cipher', fillpattern=(1, 3, 2)) - 'hereissometexttoencipher' - >>> amsco_transposition_decipher('hxomeiphscerettoisenteer', 'cipher', fillpattern=(1, 3, 2), fillstyle=AmscoFillStyle.continuous) - 'hereissometexttoencipher' - """ - - grid = amsco_transposition_positions(message, keyword, - fillpattern=fillpattern, fillstyle=fillstyle) - transposed_sections = [s for c in [l for l in zip(*grid)] for s in c] - plaintext_list = [''] * len(transposed_sections) - current_pos = 0 - for slice in transposed_sections: - plaintext_list[slice.index] = message[current_pos:current_pos-slice.start+slice.end][:len(message[slice.start:slice.end])] - current_pos += len(message[slice.start:slice.end]) - return cat(plaintext_list) - - -def amsco_break(message, translist=transpositions, patterns = [(1, 2), (2, 1)], - fillstyles = [AmscoFillStyle.continuous, - AmscoFillStyle.same_each_row, - AmscoFillStyle.reverse_each_row], - fitness=Pbigrams, - chunksize=500): - """Breaks an AMSCO transposition cipher using a dictionary and - n-gram frequency analysis - - >>> amsco_break(amsco_transposition_encipher(sanitise( \ - "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']}, \ - patterns=[(1, 2)]) # doctest: +ELLIPSIS - (((2, 0, 5, 3, 1, 4, 6), (1, 2), ), -709.4646722...) - >>> amsco_break(amsco_transposition_encipher(sanitise( \ - "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', fillpattern=(2, 1)), \ - 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']}, \ - patterns=[(1, 2), (2, 1)], fitness=Ptrigrams) # doctest: +ELLIPSIS - (((2, 0, 5, 3, 1, 4, 6), (2, 1), ), -997.0129085...) - """ - with multiprocessing.Pool() as pool: - helper_args = [(message, trans, pattern, fillstyle, fitness) - for trans in translist - for pattern in patterns - for fillstyle in fillstyles] - # Gotcha: the helper function here needs to be defined at the top level - # (limitation of Pool.starmap) - breaks = pool.starmap(amsco_break_worker, helper_args, chunksize) - return max(breaks, key=lambda k: k[1]) - -def amsco_break_worker(message, transposition, - pattern, fillstyle, fitness): - plaintext = amsco_transposition_decipher(message, transposition, - fillpattern=pattern, fillstyle=fillstyle) - fit = fitness(sanitise(plaintext)) - logger.debug('AMSCO transposition break attempt using key {0} and pattern' - '{1} ({2}) gives fit of {3} and decrypt starting: ' - '{4}'.format( - transposition, pattern, fillstyle, fit, - sanitise(plaintext)[:50])) - return (transposition, pattern, fillstyle), fit - -if __name__ == "__main__": - import doctest \ No newline at end of file