- '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']}, \
- target_counts=normalised_english_trigram_counts) # doctest: +ELLIPSIS
- ((2, 0, 5, 3, 1, 4, 6), 0.0592259560...)
- """
- ngram_length = len(next(iter(target_counts.keys())))
- with Pool() as pool:
- helper_args = [(message, trans, metric, target_counts, ngram_length,
- message_frequency_scaling)
- for trans in translist.keys()]
- # 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])
-
-def column_transposition_break_worker(message, transposition, metric, target_counts,
- ngram_length, message_frequency_scaling):
- plaintext = column_transposition_decipher(message, transposition)
- 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, fit
-
-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...)