- helper_args = [(message, trans, columnwise, metric, target_counts, ngram_length,
- message_frequency_scaling)
- for trans in translist.keys() for columnwise in [True, False]]
- # Gotcha: the helper function here needs to be defined at the top level
- # (limitation of Pool.starmap)
- breaks = pool.starmap(column_transposition_break_worker, helper_args, chunksize)
- return min(breaks, key=lambda k: k[1])
-column_transposition_break = column_transposition_break_mp
-
-def column_transposition_break_worker(message, transposition, columnwise, metric, target_counts,
- ngram_length, message_frequency_scaling):
- plaintext = column_transposition_decipher(message, transposition, columnwise=columnwise)
- counts = message_frequency_scaling(frequencies(
- ngrams(sanitise(plaintext), ngram_length)))
- fit = metric(target_counts, counts)
- logger.debug('Column transposition break attempt using key {0} '
- 'gives fit of {1} and decrypt starting: {2}'.format(
- transposition, fit,
- sanitise(plaintext)[:50]))
- return (transposition, columnwise), fit
-
-
-def transposition_break_exhaustive(message):
- best_transposition = ''
- best_pw = -float('inf')
- for keylength in range(1, 21):
- if len(message) % keylength == 0:
- for transposition in permutations(range(keylength)):
- for columnwise in [True, False]:
- plaintext = column_transposition_decipher(message,
- transposition, columnwise=columnwise)
- # pw = Pwords(segment(plaintext))
- pw = sum([log10(bigram_likelihood(b,
- normalised_english_bigram_counts,
- normalised_english_counts))
- for b in ngrams(plaintext, 2)])
- logger.debug('Column transposition break attempt using key {0} {1} '
- 'gives fit of {2} and decrypt starting: {3}'.format(
- transposition, columnwise, pw,
- sanitise(plaintext)[:50]))
- if pw > best_pw:
- best_transposition = transposition
- best_columnwise = columnwise
- best_pw = pw
- return (best_transposition, best_columnwise), best_pw
-
-
-def vigenere_keyword_break(message,
- wordlist=keywords,
- metric=norms.euclidean_distance,
- target_counts=normalised_english_counts,
- message_frequency_scaling=norms.normalise):
- """Breaks a vigenere cipher using a dictionary and
- frequency analysis
-
- >>> vigenere_keyword_break(vigenere_encipher(sanitise('this is a test ' \
- 'message for the vigenere decipherment'), 'cat'), \
- wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS
- ('cat', 0.15965224935...)
- """
- best_keyword = ''
- best_fit = float("inf")
- for keyword in wordlist:
- plaintext = vigenere_decipher(message, keyword)
- counts = message_frequency_scaling(letter_frequencies(plaintext))
- fit = metric(target_counts, counts)
- logger.debug('Vigenere break attempt using key {0} '
- 'gives fit of {1} and decrypt starting: {2}'.format(
- keyword, fit,
- sanitise(plaintext)[:50]))
- if fit < best_fit:
- best_fit = fit
- best_keyword = keyword
- logger.info('Vigenere break best fit with key {0} gives fit '
- 'of {1} and decrypt starting: {2}'.format(best_keyword,
- best_fit, sanitise(
- vigenere_decipher(message, best_keyword))[:50]))
- return best_keyword, best_fit
-
-def vigenere_keyword_break_mp(message,
- wordlist=keywords,
- metric=norms.euclidean_distance,
- target_counts=normalised_english_counts,
- message_frequency_scaling=norms.normalise,
- chunksize=500):
- """Breaks a vigenere cipher using a dictionary and
- frequency analysis
-
- >>> 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.159652249358...)
- """
- with Pool() as pool:
- helper_args = [(message, word, metric, target_counts,
- message_frequency_scaling)
- for word in wordlist]
- # Gotcha: the helper function here needs to be defined at the top level
- # (limitation of Pool.starmap)
- breaks = pool.starmap(vigenere_keyword_break_worker, helper_args, chunksize)
- return min(breaks, key=lambda k: k[1])
-
-def vigenere_keyword_break_worker(message, keyword, metric, target_counts,
- message_frequency_scaling):
- plaintext = vigenere_decipher(message, keyword)
- counts = message_frequency_scaling(letter_frequencies(plaintext))
- fit = metric(target_counts, counts)
- logger.debug('Vigenere keyword break attempt using key {0} gives fit of '
- '{1} and decrypt starting: {2}'.format(keyword,
- fit, sanitise(plaintext)[:50]))
- return keyword, fit