X-Git-Url: https://git.njae.me.uk/?a=blobdiff_plain;f=cipher.py;h=eb0a169162851fff86bd6dfe6be3cf8064608dbe;hb=ba7f680356ba1c48338814cd8164d667c82dc27d;hp=f7e3ece485cf1800a1e1d703959b10f05b217cf2;hpb=f660b5cf85c55e418f2e9f8dc359304714821b33;p=cipher-tools.git diff --git a/cipher.py b/cipher.py index f7e3ece..eb0a169 100644 --- a/cipher.py +++ b/cipher.py @@ -632,6 +632,93 @@ def railfence_decipher(message, height, fillvalue=''): up_rows.reverse() return ''.join(c for r in zip_longest(*(down_rows + up_rows), fillvalue='') for c in r) +def make_cadenus_keycolumn(doubled_letters = 'vw', start='a', reverse=False): + """Makes the key column for a Cadenus cipher (the column down between the + rows of letters) + + >>> make_cadenus_keycolumn()['a'] + 0 + >>> make_cadenus_keycolumn()['b'] + 1 + >>> make_cadenus_keycolumn()['c'] + 2 + >>> make_cadenus_keycolumn()['v'] + 21 + >>> make_cadenus_keycolumn()['w'] + 21 + >>> make_cadenus_keycolumn()['z'] + 24 + >>> make_cadenus_keycolumn(doubled_letters='ij', start='b', reverse=True)['a'] + 1 + >>> make_cadenus_keycolumn(doubled_letters='ij', start='b', reverse=True)['b'] + 0 + >>> make_cadenus_keycolumn(doubled_letters='ij', start='b', reverse=True)['c'] + 24 + >>> make_cadenus_keycolumn(doubled_letters='ij', start='b', reverse=True)['i'] + 18 + >>> make_cadenus_keycolumn(doubled_letters='ij', start='b', reverse=True)['j'] + 18 + >>> make_cadenus_keycolumn(doubled_letters='ij', start='b', reverse=True)['v'] + 6 + >>> make_cadenus_keycolumn(doubled_letters='ij', start='b', reverse=True)['z'] + 2 + """ + index_to_remove = string.ascii_lowercase.find(doubled_letters[0]) + short_alphabet = string.ascii_lowercase[:index_to_remove] + string.ascii_lowercase[index_to_remove+1:] + if reverse: + short_alphabet = ''.join(reversed(short_alphabet)) + start_pos = short_alphabet.find(start) + rotated_alphabet = short_alphabet[start_pos:] + short_alphabet[:start_pos] + keycolumn = {l: i for i, l in enumerate(rotated_alphabet)} + keycolumn[doubled_letters[0]] = keycolumn[doubled_letters[1]] + return keycolumn + +def cadenus_encipher(message, keyword, keycolumn, fillvalue='a'): + """Encipher with the Cadenus cipher + + >>> cadenus_encipher(sanitise('Whoever has made a voyage up the Hudson ' \ + 'must remember the Kaatskill mountains. ' \ + 'They are a dismembered branch of the great'), \ + 'wink', \ + make_cadenus_keycolumn(doubled_letters='vw', start='a', reverse=True)) + 'antodeleeeuhrsidrbhmhdrrhnimefmthgeaetakseomehetyaasuvoyegrastmmuuaeenabbtpchehtarorikswosmvaleatned' + >>> cadenus_encipher(sanitise('a severe limitation on the usefulness of ' \ + 'the cadenus is that every message must be ' \ + 'a multiple of twenty-five letters long'), \ + 'easy', \ + make_cadenus_keycolumn(doubled_letters='vw', start='a', reverse=True)) + 'systretomtattlusoatleeesfiyheasdfnmschbhneuvsnpmtofarenuseieeieltarlmentieetogevesitfaisltngeeuvowul' + """ + rows = chunks(message, len(message) // 25, fillvalue=fillvalue) + columns = zip(*rows) + rotated_columns = [col[start:] + col[:start] for start, col in zip([keycolumn[l] for l in keyword], columns)] + rotated_rows = zip(*rotated_columns) + transpositions = transpositions_of(keyword) + transposed = [transpose(r, transpositions) for r in rotated_rows] + return ''.join(chain(*transposed)) + +def cadenus_decipher(message, keyword, keycolumn, fillvalue='a'): + """ + >>> cadenus_decipher('antodeleeeuhrsidrbhmhdrrhnimefmthgeaetakseomehetyaa' \ + 'suvoyegrastmmuuaeenabbtpchehtarorikswosmvaleatned', \ + 'wink', \ + make_cadenus_keycolumn(reverse=True)) + 'whoeverhasmadeavoyageupthehudsonmustrememberthekaatskillmountainstheyareadismemberedbranchofthegreat' + >>> cadenus_decipher('systretomtattlusoatleeesfiyheasdfnmschbhneuvsnpmtof' \ + 'arenuseieeieltarlmentieetogevesitfaisltngeeuvowul', \ + 'easy', \ + make_cadenus_keycolumn(reverse=True)) + 'aseverelimitationontheusefulnessofthecadenusisthateverymessagemustbeamultipleoftwentyfiveletterslong' + """ + rows = chunks(message, len(message) // 25, fillvalue=fillvalue) + transpositions = transpositions_of(keyword) + untransposed_rows = [untranspose(r, transpositions) for r in rows] + columns = zip(*untransposed_rows) + rotated_columns = [col[-start:] + col[:-start] for start, col in zip([keycolumn[l] for l in keyword], columns)] + rotated_rows = zip(*rotated_columns) + # return rotated_columns + return ''.join(chain(*rotated_rows)) + def hill_encipher(matrix, message_letters, fillvalue='a'): """Hill cipher @@ -676,8 +763,14 @@ def hill_decipher(matrix, message, fillvalue='a'): # 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 @@ -714,17 +807,25 @@ def amsco_transposition_positions(message, keyword, current_position = 0 grid = [] + current_fillpattern = fillpattern while current_position < message_length: row = [] + if fillstyle == AmscoFillStyle.same_each_row: + fill_iterator = cycle(fillpattern) + if fillstyle == AmscoFillStyle.reverse_each_row: + fill_iterator = 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)): +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)) @@ -742,12 +843,14 @@ def amsco_transposition_encipher(message, keyword, fillpattern=(1,2)): >>> amsco_transposition_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 3, 2)) 'hxomeiphscerettoisenteer' """ - grid = amsco_transposition_positions(message, keyword, fillpattern=fillpattern) + 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)): +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)) @@ -766,7 +869,8 @@ def amsco_transposition_decipher(message, keyword, fillpattern=(1,2)): 'hereissometexttoencipher' """ - grid = amsco_transposition_positions(message, keyword, fillpattern=fillpattern) + 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