X-Git-Url: https://git.njae.me.uk/?a=blobdiff_plain;f=szyfrow%2Fbifid.py;h=f21cce92e125ba6af4cf8c529a6162950bc6ee38;hb=refs%2Fheads%2Fmain;hp=ed8462febbea43927ca3c775f8b98b372e26536f;hpb=27c8005f6dea0026887b80a01b5f93a8f1b3c2b2;p=szyfrow.git diff --git a/szyfrow/bifid.py b/szyfrow/bifid.py index ed8462f..f21cce9 100644 --- a/szyfrow/bifid.py +++ b/szyfrow/bifid.py @@ -1,3 +1,6 @@ +"""Enciphering and deciphering using the [Bifid cipher](https://en.wikipedia.org/wiki/Bifid_cipher). +Also attempts to break messages that use a Bifid cipher. +""" import multiprocessing from szyfrow.support.utilities import * from szyfrow.support.language_models import * @@ -28,14 +31,24 @@ def bifid_encipher(message, keyword, wrap_alphabet=KeywordWrapAlphabet.from_a, >>> bifid_encipher("indiacurry", 'iguana', period=4, fillvalue='x') 'ibnhgaqltzml' """ + + if period: + if not fillvalue: + raise ValueError("fillvalue must be given if period is given") + else: + p_message = message + pad(len(message), period, fillvalue) + else: + p_message = message + translation, f_grid, r_grid = bifid_grid(keyword, wrap_alphabet, letter_mapping) - t_message = message.translate(translation) + t_message = p_message.translate(translation) pairs0 = [f_grid[l] for l in sanitise(t_message)] if period: - chunked_pairs = [pairs0[i:i+period] for i in range(0, len(pairs0), period)] - if len(chunked_pairs[-1]) < period and fillvalue: - chunked_pairs[-1] += [f_grid[fillvalue]] * (period - len(chunked_pairs[-1])) + # chunked_pairs = [pairs0[i:i+period] for i in range(0, len(pairs0), period)] + # if len(chunked_pairs[-1]) < period and fillvalue: + # chunked_pairs[-1] += [f_grid[fillvalue]] * (period - len(chunked_pairs[-1])) + chunked_pairs = chunks(pairs0, period, fillvalue=None) else: chunked_pairs = [pairs0] @@ -59,14 +72,23 @@ def bifid_decipher(message, keyword, wrap_alphabet=KeywordWrapAlphabet.from_a, >>> bifid_decipher("ibnhgaqltzml", 'iguana', period=4) 'indiacurryxx' """ + if period: + if not fillvalue: + raise ValueError("fillvalue must be given if period is given") + else: + p_message = message + pad(len(message), period, fillvalue) + else: + p_message = message + translation, f_grid, r_grid = bifid_grid(keyword, wrap_alphabet, letter_mapping) t_message = message.translate(translation) pairs0 = [f_grid[l] for l in sanitise(t_message)] if period: - chunked_pairs = [pairs0[i:i+period] for i in range(0, len(pairs0), period)] - if len(chunked_pairs[-1]) < period and fillvalue: - chunked_pairs[-1] += [f_grid[fillvalue]] * (period - len(chunked_pairs[-1])) + # chunked_pairs = [pairs0[i:i+period] for i in range(0, len(pairs0), period)] + # if len(chunked_pairs[-1]) < period and fillvalue: + # chunked_pairs[-1] += [f_grid[fillvalue]] * (period - len(chunked_pairs[-1])) + chunked_pairs = chunks(pairs0, period, fillvalue=None) else: chunked_pairs = [pairs0] @@ -80,22 +102,28 @@ def bifid_decipher(message, keyword, wrap_alphabet=KeywordWrapAlphabet.from_a, return cat(r_grid[p] for p in pairs1) -def bifid_break_mp(message, wordlist=keywords, fitness=Pletters, max_period=10, +def bifid_break(message, wordlist=None, fitness=Pletters, max_period=10, number_of_solutions=1, chunksize=500): """Breaks a keyword substitution cipher using a dictionary and frequency analysis - >>> bifid_break_mp(bifid_encipher('this is a test message for the ' \ + If `wordlist` is not specified, use + [`szyfrow.support.langauge_models.keywords`](support/language_models.html#szyfrow.support.language_models.keywords). + + >>> bifid_break(bifid_encipher('this is a test message for the ' \ 'keyword decipherment', 'elephant', wrap_alphabet=KeywordWrapAlphabet.from_last), \ wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS (('elephant', , 0), -52.834575011...) - >>> bifid_break_mp(bifid_encipher('this is a test message for the ' \ + >>> bifid_break(bifid_encipher('this is a test message for the ' \ 'keyword decipherment', 'elephant', wrap_alphabet=KeywordWrapAlphabet.from_last), \ wordlist=['cat', 'elephant', 'kangaroo'], \ number_of_solutions=2) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE [(('elephant', , 0), -52.834575011...), (('elephant', , 0), -52.834575011...)] """ + if wordlist is None: + wordlist = keywords + with multiprocessing.Pool() as pool: helper_args = [(message, word, wrap, period, fitness) for word in wordlist @@ -110,7 +138,8 @@ def bifid_break_mp(message, wordlist=keywords, fitness=Pletters, max_period=10, return sorted(breaks, key=lambda k: k[1], reverse=True)[:number_of_solutions] def bifid_break_worker(message, keyword, wrap_alphabet, period, fitness): - plaintext = bifid_decipher(message, keyword, wrap_alphabet, period=period) + plaintext = bifid_decipher(message, keyword, wrap_alphabet, + period=period, fillvalue='e') fit = fitness(plaintext) return (keyword, wrap_alphabet, period), fit