From f19a021eabb3222709b9d513839a14c01cfdfd38 Mon Sep 17 00:00:00 2001 From: Neil Smith Date: Sat, 14 Nov 2020 16:52:48 +0000 Subject: [PATCH] Testing done for now. --- setup.py | 2 +- szyfrow/cadenus.py | 44 +++++++------ szyfrow/hill.py | 76 ---------------------- szyfrow/playfair.py | 12 ++-- szyfrow/polybius.py | 10 +-- tests/test_cadenus.py | 58 +++++++++++++---- tests/test_playfair.py | 123 ++++++++++++++++++++++++++++++++++++ tests/test_pocket_enigma.py | 72 +++++++++++++++++++++ tests/test_polybius.py | 100 +++++++++++++++++++++++++++++ 9 files changed, 381 insertions(+), 116 deletions(-) delete mode 100644 szyfrow/hill.py create mode 100644 tests/test_playfair.py create mode 100644 tests/test_pocket_enigma.py create mode 100644 tests/test_polybius.py diff --git a/setup.py b/setup.py index b8f910b..e585ed4 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ setuptools.setup( "Topic :: Security :: Cryptography", ], python_requires='>=3.7', - install_requires=['numpy'], + install_requires=[], include_package_data=True, setup_requires=['pytest-runner', 'numpy'], tests_require=['pytest'], diff --git a/szyfrow/cadenus.py b/szyfrow/cadenus.py index b56500c..b0a0138 100644 --- a/szyfrow/cadenus.py +++ b/szyfrow/cadenus.py @@ -62,13 +62,17 @@ def cadenus_encipher(message, keyword, keycolumn, fillvalue='a'): 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 cat(chain(*transposed)) + enciphered_chunks = [] + for message_chunk in chunks(message, len(transpositions) * 25, + fillvalue=fillvalue): + rows = chunks(message_chunk, len(transpositions), 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) + transposed = [transpose(r, transpositions) for r in rotated_rows] + enciphered_chunks.append(cat(chain(*transposed))) + return cat(enciphered_chunks) def cadenus_decipher(message, keyword, keycolumn, fillvalue='a'): """ @@ -83,28 +87,32 @@ def cadenus_decipher(message, keyword, keycolumn, fillvalue='a'): 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 cat(chain(*rotated_rows)) + deciphered_chunks = [] + for message_chunk in chunks(message, len(transpositions) * 25, + fillvalue=fillvalue): + rows = chunks(message_chunk, len(transpositions), fillvalue=fillvalue) + 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) + deciphered_chunks.append(cat(chain(*rotated_rows))) + return cat(deciphered_chunks) + -def cadenus_break(message, words=keywords, +def cadenus_break(message, wordlist=keywords, doubled_letters='vw', fitness=Pbigrams): - c = make_cadenus_keycolumn(reverse=True) - valid_words = [w for w in words - if len(transpositions_of(w)) == len(message) // 25] + # c = make_cadenus_keycolumn(reverse=True) + # valid_words = [w for w in wordlist + # if len(transpositions_of(w)) == len(message) // 25] with multiprocessing.Pool() as pool: results = pool.starmap(cadenus_break_worker, [(message, w, make_cadenus_keycolumn(doubled_letters=doubled_letters, start=s, reverse=r), fitness) - for w in words + for w in wordlist for s in string.ascii_lowercase for r in [True, False] # if max(transpositions_of(w)) <= len( diff --git a/szyfrow/hill.py b/szyfrow/hill.py deleted file mode 100644 index 4f5baef..0000000 --- a/szyfrow/hill.py +++ /dev/null @@ -1,76 +0,0 @@ -import multiprocessing -import numpy as np -from numpy import matrix -from numpy import linalg -from szyfrow.support.utilities import * -from szyfrow.support.language_models import * -from szyfrow.affine import modular_division_table - - -def hill_encipher(matrix, message_letters, fillvalue='a'): - """Hill cipher - - >>> hill_encipher(np.matrix([[7,8], [11,11]]), 'hellothere') - 'drjiqzdrvx' - >>> hill_encipher(np.matrix([[6, 24, 1], [13, 16, 10], [20, 17, 15]]), \ - 'hello there') - 'tfjflpznvyac' - """ - n = len(matrix) - sanitised_message = sanitise(message_letters) - if len(sanitised_message) % n != 0: - padding = fillvalue[0] * (n - len(sanitised_message) % n) - else: - padding = '' - message = [pos(c) for c in sanitised_message + padding] - message_chunks = [message[i:i+n] for i in range(0, len(message), n)] - # message_chunks = chunks(message, len(matrix), fillvalue=None) - enciphered_chunks = [((matrix * np.matrix(c).T).T).tolist()[0] - for c in message_chunks] - return cat([unpos(round(l)) - for l in sum(enciphered_chunks, [])]) - -def hill_decipher(matrix, message, fillvalue='a'): - """Hill cipher - - >>> hill_decipher(np.matrix([[7,8], [11,11]]), 'drjiqzdrvx') - 'hellothere' - >>> hill_decipher(np.matrix([[6, 24, 1], [13, 16, 10], [20, 17, 15]]), \ - 'tfjflpznvyac') - 'hellothereaa' - """ - adjoint = linalg.det(matrix)*linalg.inv(matrix) - inverse_determinant = modular_division_table[int(round(linalg.det(matrix))) % 26, 1] - inverse_matrix = (inverse_determinant * adjoint) % 26 - return hill_encipher(inverse_matrix, message, fillvalue) - -def hill_break(message, matrix_size=2, fitness=Pletters, - number_of_solutions=1, chunksize=500): - - all_matrices = [np.matrix(list(m)) - for m in itertools.product([list(r) - for r in itertools.product(range(26), repeat=matrix_size)], - repeat=matrix_size)] - valid_matrices = [m for m, d in - zip(all_matrices, (int(round(linalg.det(m))) for m in all_matrices)) - if d != 0 - if d % 2 != 0 - if d % 13 != 0 ] - with multiprocessing.Pool() as pool: - helper_args = [(message, matrix, fitness) - for matrix in valid_matrices] - # Gotcha: the helper function here needs to be defined at the top level - # (limitation of Pool.starmap) - breaks = pool.starmap(hill_break_worker, helper_args, chunksize) - if number_of_solutions == 1: - return max(breaks, key=lambda k: k[1]) - else: - return sorted(breaks, key=lambda k: k[1], reverse=True)[:number_of_solutions] - -def hill_break_worker(message, matrix, fitness): - plaintext = hill_decipher(matrix, message) - fit = fitness(plaintext) - return matrix, fit - -if __name__ == "__main__": - import doctest \ No newline at end of file diff --git a/szyfrow/playfair.py b/szyfrow/playfair.py index b903ec0..e6939db 100644 --- a/szyfrow/playfair.py +++ b/szyfrow/playfair.py @@ -85,8 +85,9 @@ def playfair_encipher(message, keyword, padding_letter='x', grid = polybius_grid(keyword, column_order, row_order, letters_to_merge=letters_to_merge, wrap_alphabet=wrap_alphabet) - message_bigrams = playfair_bigrams(sanitise(message), padding_letter=padding_letter, - padding_replaces_repeat=padding_replaces_repeat) + message_bigrams = playfair_bigrams( + sanitise(message), padding_letter=padding_letter, + padding_replaces_repeat=padding_replaces_repeat) ciphertext_bigrams = [playfair_encipher_bigram(b, grid, padding_letter=padding_letter) for b in message_bigrams] return cat(ciphertext_bigrams) @@ -100,12 +101,13 @@ def playfair_decipher(message, keyword, padding_letter='x', grid = polybius_grid(keyword, column_order, row_order, letters_to_merge=letters_to_merge, wrap_alphabet=wrap_alphabet) - message_bigrams = playfair_bigrams(sanitise(message), padding_letter=padding_letter, - padding_replaces_repeat=padding_replaces_repeat) + message_bigrams = playfair_bigrams( + sanitise(message), padding_letter=padding_letter, + padding_replaces_repeat=padding_replaces_repeat) plaintext_bigrams = [playfair_decipher_bigram(b, grid, padding_letter=padding_letter) for b in message_bigrams] return cat(plaintext_bigrams) -def playfair_break_mp(message, +def playfair_break(message, letters_to_merge=None, padding_letter='x', wordlist=keywords, fitness=Pletters, number_of_solutions=1, chunksize=500): diff --git a/szyfrow/polybius.py b/szyfrow/polybius.py index cd78ee9..b3ffeab 100644 --- a/szyfrow/polybius.py +++ b/szyfrow/polybius.py @@ -48,7 +48,7 @@ def polybius_reverse_grid(keyword, column_order, row_order, letters_to_merge=Non def polybius_flatten(pair, column_first): - """Convert a series of pairs into a single list of characters""" + """Convert a pair of characters into a single string.""" if column_first: return str(pair[1]) + str(pair[0]) else: @@ -101,13 +101,15 @@ def polybius_decipher(message, keyword, column_order, row_order, column_index_type = type(column_order[0]) row_index_type = type(row_order[0]) if column_first: - pairs = [(column_index_type(p[1]), row_index_type(p[0])) for p in chunks(message, 2)] + pairs = [(column_index_type(p[1]), row_index_type(p[0])) + for p in chunks(message, 2)] else: - pairs = [(row_index_type(p[0]), column_index_type(p[1])) for p in chunks(message, 2)] + pairs = [(row_index_type(p[0]), column_index_type(p[1])) + for p in chunks(message, 2)] return cat(grid[p] for p in pairs if p in grid) -def polybius_break_mp(message, column_labels, row_labels, +def polybius_break(message, column_labels, row_labels, letters_to_merge=None, wordlist=keywords, fitness=Pletters, number_of_solutions=1, chunksize=500): diff --git a/tests/test_cadenus.py b/tests/test_cadenus.py index c7b48ec..c7325e9 100644 --- a/tests/test_cadenus.py +++ b/tests/test_cadenus.py @@ -56,26 +56,60 @@ def test_decipher_message(): def test_cadenus_break(): - plaintext = 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.''') + plaintext = sanitise('''You will rejoice to hear that no disaster has accompanied the +commencement of an enterprise which you have regarded with such evil +forebodings. I arrived here yesterday, and my first task is to assure +my dear sister of my welfare and increasing confidence in the success +of my undertaking. + +I am already far north of London, and as I walk in the streets of +Petersburgh, I feel a cold northern breeze play upon my cheeks, which +braces my nerves and fills me with delight. Do you understand this +feeling? This breeze, which has travelled from the regions towards +which I am advancing, gives me a foretaste of those icy climes. +Inspirited by this wind of promise, my daydreams become more fervent +and vivid. + +I try in vain to be persuaded that the pole is the seat of +frost and desolation; it ever presents itself to my imagination as the +region of beauty and delight. There, Margaret, the sun is for ever +visible, its broad disk just skirting the horizon and diffusing a +perpetual splendour. There—for with your leave, my sister, I will put +some trust in preceding navigators—there snow and frost are banished; +and, sailing over a calm sea, we may be wafted to a land surpassing in +wonders and in beauty every region hitherto discovered on the habitable +globe. + +Its productions and features may be without example, as the +phenomena of the heavenly bodies undoubtedly are in those undiscovered +solitudes. What may not be expected in a country of eternal light? I +may there discover the wondrous power which attracts the needle and may +regulate a thousand celestial observations that require only this +voyage to render their seeming eccentricities consistent for ever. I +shall satiate my ardent curiosity with the sight of a part of the world +never before visited, and may tread a land never before imprinted by +the foot of man. These are my enticements, and they are sufficient to +conquer all fear of danger or death and to induce me to commence this +laborious voyage with the joy a child feels when he embarks in a little +boat, with his holiday mates, on an expedition of discovery up his +native river. ''' +) expected_key = 'swashbuckling' - expected_score = Ptrigrams(plaintext) + chunk_len = len(transpositions_of(expected_key)) * 25 + target = plaintext + pad(len(plaintext), chunk_len, 'a') + expected_score = Ptrigrams(target) expected_keycol = make_cadenus_keycolumn(doubled_letters='vw', start='a', reverse=True) ciphertext = cadenus_encipher(plaintext, expected_key, expected_keycol) - # dictionary = ['clearinghouse', 'computerising', 'counterclaims', - # 'housewarmings', 'intravenously', 'liquefactions', 'somersaulting', - # 'sportsmanlike', 'swashbuckling'] + dictionary = ['clearinghouse', 'computerising', 'counterclaims', + 'housewarmings', 'intravenously', 'liquefactions', 'somersaulting', + 'sportsmanlike', 'swashbuckling'] - dictionary = ['swashbuckling'] + # dictionary = ['swashbuckling'] - (key, keycol), score = cadenus_break(ciphertext, words=dictionary, + (key, keycol), score = cadenus_break(ciphertext, wordlist=dictionary, fitness=Ptrigrams) assert key == expected_key diff --git a/tests/test_playfair.py b/tests/test_playfair.py new file mode 100644 index 0000000..2ce1264 --- /dev/null +++ b/tests/test_playfair.py @@ -0,0 +1,123 @@ +import pytest +import string + +from szyfrow.playfair import * +from szyfrow.support.utilities import * + +def test_wrap(): + assert playfair_wrap(3, 0, 4) == 3 + assert playfair_wrap(0, 0, 4) == 0 + assert playfair_wrap(4, 0, 4) == 4 + assert playfair_wrap(7, 0, 4) == 2 + assert playfair_wrap(-1, 0, 4) == 4 + +def test_encipher_bigram(): + grid = polybius_grid('playfairexample', list(range(5)), list(range(5))) + + plain_pairs = 'hi de th eg ol di nt he tr ex es tu mp'.split() + cipher_pairs = 'bm od zb xd na be ku dm ui xm mo uv if'.split() + + for p, c in zip(plain_pairs, cipher_pairs): + assert playfair_encipher_bigram(p, grid) == c + +def test_decipher_bigram(): + grid = polybius_grid('playfairexample', list(range(5)), list(range(5))) + + plain_pairs = 'hi de th eg ol di nt he tr ex es tu mp'.split() + cipher_pairs = 'bm od zb xd na be ku dm ui xm mo uv if'.split() + + for p, c in zip(plain_pairs, cipher_pairs): + assert playfair_decipher_bigram(c, grid) == p + +def test_playfair_bigrams(): + txt = sanitise('hide the gold in the tree stumps') + f_bigrams = 'hi de th eg ol di nt he tr ex es tu mp sx'.split() + t_bigrams = 'hi de th eg ol di nt he tr ex st um ps'.split() + assert playfair_bigrams(txt, padding_replaces_repeat=True) == t_bigrams + assert playfair_bigrams(txt, padding_replaces_repeat=False) == f_bigrams + + + +def test_encipher_message(): + ct = playfair_encipher('hide the gold in the tree stump', + 'playfairexample', wrap_alphabet=KeywordWrapAlphabet.from_a) + assert ct == 'bmodzbxdnabekudmuixmmouvif' + + ct = playfair_encipher('this is a test message for the ' \ + 'playfair decipherment', 'elephant') + assert ct == 'clkrkrldhodghouhflovqbalhphzmerxnabkhapofatb' + + ct = playfair_encipher('this is a test message for the ' \ + 'playfair decipherment', 'elephant', + wrap_alphabet=KeywordWrapAlphabet.from_last) + assert ct == 'vlkrkrlwamnoamawdpolovalhplcklrhmnbkhahmentu' + + ct = playfair_encipher('''Whoever has made a voyage up the Hudson must + remember the Kaatskill mountains. They are a dismembered branch of + the great''', 'hudson') + assert ct == 'vuelznmscupnobnwszgpaoqmonudsohephortctnqnctmoclbwepcrkffthdembgchmoczcpnbbqhrntcntcbipcaeuhlmonkpnbqz' + + +def test_decipher_message(): + plaintext = sanitise('''Whoever has made a voyage up the Hudson must + remember the Kaatskill mountains. They are a dismembered branch of + the great''') + + trans_table = ''.maketrans('j', 'i') + + for key in ['wink', 'elephant', 'swashbuckling']: + for prp in [True, False]: + expected = cat(playfair_bigrams(plaintext.translate(trans_table), + padding_replaces_repeat=prp)) + for wrap in KeywordWrapAlphabet: + enciphered = playfair_encipher(plaintext, key, + padding_replaces_repeat=prp, wrap_alphabet=wrap) + deciphered = playfair_decipher(enciphered, key, + padding_replaces_repeat=prp, wrap_alphabet=wrap) + assert deciphered == expected + + +def test_playfair_break(): + plaintext = sanitise('''You will rejoice to hear that no disaster has accompanied the + commencement of an enterprise which you have regarded with such evil + forebodings. I arrived here yesterday, and my first task is to assure + my dear sister of my welfare and increasing confidence in the success + of my undertaking.''') + expected_key = 'swashbuckling' + expected_wrap = KeywordWrapAlphabet.from_last + expected_letters_to_merge = {'j': 'i'} + expected_padding_letter = 'x' + expected_padding_replaces_repeat = False + ciphertext = playfair_encipher(plaintext, expected_key, + padding_letter=expected_padding_letter, + padding_replaces_repeat=expected_padding_replaces_repeat, + letters_to_merge=expected_letters_to_merge, + wrap_alphabet=expected_wrap) + + + exptected_text = playfair_decipher(ciphertext, expected_key, + padding_letter=expected_padding_letter, + padding_replaces_repeat=expected_padding_replaces_repeat, + letters_to_merge=expected_letters_to_merge, + wrap_alphabet=expected_wrap) + + expected_score = Ptrigrams(exptected_text) + + dictionary = ['clearinghouse', 'computerising', 'counterclaims', + 'housewarmings', 'intravenously', 'liquefactions', 'somersaulting', + 'sportsmanlike', 'swashbuckling'] + + # dictionary = ['swashbuckling'] + + (key, wrap, letters_to_merge, padding_letter, padding_replaces_repeat), score = playfair_break( + ciphertext, + letters_to_merge=expected_letters_to_merge, + padding_letter=expected_padding_letter, + wordlist=dictionary, fitness=Ptrigrams) + + assert key == expected_key + assert wrap == expected_wrap + assert letters_to_merge == expected_letters_to_merge + assert padding_letter == expected_padding_letter + assert padding_replaces_repeat == expected_padding_replaces_repeat + assert score == pytest.approx(expected_score) diff --git a/tests/test_pocket_enigma.py b/tests/test_pocket_enigma.py new file mode 100644 index 0000000..21971fc --- /dev/null +++ b/tests/test_pocket_enigma.py @@ -0,0 +1,72 @@ +import pytest +import string + +from szyfrow.pocket_enigma import * +from szyfrow.support.utilities import * + +@pytest.fixture +def pe(): + return PocketEnigma(1, 'a') + +def test_init(pe): + assert pe.wheel_map == [25, 4, 23, 10, 1, 7, 9, 5, 12, 6, 3, 17, 8, 14, 13, 21, 19, 11, 20, 16, 18, 15, 24, 2, 22, 0] + assert pe.position == 0 + +def test_wheel_map(pe): + assert pe.make_wheel_map(pe.wheel2) == [2, 3, 0, 1, 22, 8, 15, 12, 5, 10, 9, 13, 7, 11, 16, 6, 14, 25, 20, 21, 18, 19, 4, 24, 23, 17] + +def test_validate_wheel_map(pe): + pe.validate_wheel_spec(pe.wheel2) == True + + with pytest.raises(ValueError): + pe.validate_wheel_spec([]) + with pytest.raises(ValueError): + pe.validate_wheel_spec([('a', 'b', 'c')]*13) + with pytest.raises(ValueError): + pe.validate_wheel_spec([('a', 'b')]*13) + +def test_encipher_letter(pe): + pe.set_position('f') + assert pe.encipher_letter('k') == 'h' + assert pe.position == 6 + +def test_lookup(pe): + pe.set_position('f') + assert cat([pe.lookup(l) for l in string.ascii_lowercase]) == 'udhbfejcpgmokrliwntsayqzvx' + assert pe.lookup('A') == '' + +def test_advance(pe): + pe.set_position('f') + assert pe.position == 5 + pe.advance() + assert pe.position == 6 + + pe.set_position('y') + assert pe.position == 24 + pe.advance() + assert pe.position == 25 + pe.advance() + assert pe.position == 0 + +def test_encipher(pe): + pe.set_position('f') + assert pe.encipher('helloworld') == 'kjsglcjoqc' + + pe.set_position('f') + assert pe.encipher('kjsglcjoqc') == 'helloworld' + + assert pe.encipher('helloworld', starting_position = 'x') == 'egrekthnnf' + +def test_set_position(pe): + assert pe.set_position('a') == 0 + assert pe.set_position('m') == 12 + assert pe.set_position('z') == 25 + +def test_break(): + assert pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'h', 0) == ['a', 'f', 'q'] + assert pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'he', 0) == ['a'] + assert pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'll', 2) == ['a'] + assert pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'l', 2) == ['a'] + assert pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'l', 3) == ['a', 'j', 'n'] + assert pocket_enigma_break_by_crib('aaaaa', 1, 'l', 3) == [] + \ No newline at end of file diff --git a/tests/test_polybius.py b/tests/test_polybius.py new file mode 100644 index 0000000..477ff34 --- /dev/null +++ b/tests/test_polybius.py @@ -0,0 +1,100 @@ +import pytest +import string + +from szyfrow.polybius import * +from szyfrow.support.utilities import * + +def test_grid(): + grid = polybius_grid('a', 'abcde', 'abcde') + assert grid['x'] == ('e', 'c') + grid = polybius_grid('elephant', 'abcde', 'abcde') + assert grid['e'] == ('a', 'a') + assert grid['b'] == ('b', 'c') + +def test_reverse_grid(): + grid = polybius_reverse_grid('a', 'abcde', 'abcde') + assert grid['e', 'c'] == 'x' + grid = polybius_reverse_grid('elephant', 'abcde', 'abcde') + assert grid['a', 'a'] == 'e' + assert grid['b', 'c'] == 'b' + +def test_grid_round_trip(): + ltm = {'j': 'i'} + fgrid = polybius_grid('elephant', 'abcde', 'abcde', + letters_to_merge=ltm) + rgrid = polybius_reverse_grid('elephant', 'abcde', 'abcde', + letters_to_merge=ltm) + for l in fgrid: + if l in ltm: + assert ltm[l] == rgrid[fgrid[l]] + else: + assert l == rgrid[fgrid[l]] + for p in rgrid: + assert p == fgrid[rgrid[p]] + + +def test_polybius_flatten(): + assert polybius_flatten(('a', 'b'), column_first=True) == 'ba' + assert polybius_flatten(('a', 'b'), column_first=False) == 'ab' + + +def test_encipher_message(): + ct = polybius_encipher('this is a test message for the ' \ + 'polybius decipherment', 'elephant', \ + [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], \ + wrap_alphabet=KeywordWrapAlphabet.from_last) + assert ct == '2214445544551522115522511155551543114252542214111352123234442355411135441314115451112122' + + ct = polybius_encipher('this is a test message for the ' \ + 'polybius decipherment', 'elephant', 'abcde', 'abcde', \ + column_first=False) + assert ct == 'bbadccddccddaebbaaddbbceaaddddaecbaacadadcbbadaaacdaabedbcccdeddbeaabdccacadaadcceaababb' + + ct = polybius_encipher('this is a test message for the ' \ + 'polybius decipherment', 'elephant', 'abcde', 'abcde', \ + column_first=True) + assert ct == 'bbdaccddccddeabbaaddbbecaaddddeabcaaacadcdbbdaaacaadbadecbccedddebaadbcccadaaacdecaaabbb' + + +def test_decipher_message(): + plaintext = sanitise('''Whoever has made a voyage up the Hudson must + remember the Kaatskill mountains. They are a dismembered branch of + the great''') + for key in ['wink', 'elephant', 'swashbuckling']: + enciphered = polybius_encipher(plaintext, key, 'abcde', 'abcde') + deciphered = polybius_decipher(enciphered, key, 'abcde', 'abcde') + assert deciphered == plaintext + + +def test_cadenus_break(): + plaintext = sanitise('''You will rejoice to hear that no disaster has accompanied the + commencement of an enterprise which you have regarded with such evil + forebodings. I arrived here yesterday, and my first task is to assure + my dear sister of my welfare and increasing confidence in the success + of my undertaking.''') + expected_key = 'swashbuckling' + expected_wrap = KeywordWrapAlphabet.from_last + expected_row_order = 'abcde' + expected_col_order = 'abcde' + expected_col_first = True + ciphertext = polybius_encipher(plaintext, expected_key, + expected_col_order, expected_row_order, + column_first=expected_col_first, wrap_alphabet=expected_wrap) + + trans_table = ''.maketrans('j', 'i') + expected_score = Ptrigrams(plaintext.translate(trans_table)) + + dictionary = ['clearinghouse', 'computerising', 'counterclaims', + 'housewarmings', 'intravenously', 'liquefactions', 'somersaulting', + 'sportsmanlike', 'swashbuckling'] + + # dictionary = ['swashbuckling'] + + (key, wrap, col_order, row_order, col_first), score = polybius_break(ciphertext, + expected_col_order, expected_row_order, + wordlist=dictionary, fitness=Ptrigrams) + + assert key == expected_key + assert wrap == expected_wrap + assert col_first == expected_col_first + assert score == pytest.approx(expected_score) -- 2.34.1